From 05d43e60953be60ed8400874f858dc27c9598111 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Tue, 30 May 2023 18:21:06 +0200 Subject: [PATCH] formatter: Remove CST and old formatting --- .../ruff_python_formatter/src/attachment.rs | 125 - .../ruff_python_formatter/src/cst/helpers.rs | 138 -- crates/ruff_python_formatter/src/cst/mod.rs | 2123 ----------------- .../ruff_python_formatter/src/cst/visitor.rs | 634 ----- .../ruff_python_formatter/src/format/alias.rs | 33 - .../ruff_python_formatter/src/format/arg.rs | 32 - .../src/format/arguments.rs | 121 - .../src/format/bool_op.rs | 34 - .../src/format/builders.rs | 101 - .../src/format/cmp_op.rs | 42 - .../src/format/comments.rs | 117 - .../src/format/comprehension.rs | 45 - .../src/format/excepthandler.rs | 39 - .../ruff_python_formatter/src/format/expr.rs | 892 ------- .../src/format/helpers.rs | 87 - .../src/format/keyword.rs | 37 - .../src/format/match_case.rs | 42 - .../ruff_python_formatter/src/format/mod.rs | 20 - .../src/format/numbers.rs | 203 -- .../src/format/operator.rs | 45 - .../src/format/pattern.rs | 153 -- .../ruff_python_formatter/src/format/stmt.rs | 946 -------- .../src/format/strings.rs | 244 -- .../src/format/unary_op.rs | 35 - .../src/format/withitem.rs | 30 - crates/ruff_python_formatter/src/lib.rs | 49 +- .../ruff_python_formatter/src/module/mod.rs | 33 + crates/ruff_python_formatter/src/newlines.rs | 370 --- .../ruff_python_formatter/src/parentheses.rs | 199 -- crates/ruff_python_formatter/src/prelude.rs | 1 + ...ttribute_access_on_number_literals_py.snap | 132 + ...__black_test__beginning_backslash_py.snap} | 39 +- ...r__tests__black_test__bracketmatch_py.snap | 54 + ...lack_test__class_blank_parentheses_py.snap | 141 ++ ...black_test__class_methods_new_line_py.snap | 559 +++++ ...er__tests__black_test__collections_py.snap | 370 +++ ...est__comment_after_escaped_newline_py.snap | 64 + ...tter__tests__black_test__comments2_py.snap | 401 ++-- ...tter__tests__black_test__comments3_py.snap | 189 -- ...tter__tests__black_test__comments4_py.snap | 435 ---- ...tter__tests__black_test__comments5_py.snap | 279 --- ...tter__tests__black_test__comments6_py.snap | 456 ---- ...tter__tests__black_test__comments8_py.snap | 47 + ...tter__tests__black_test__comments9_py.snap | 189 +- ..._test__comments_non_breaking_space_py.snap | 41 +- ...est__composition_no_trailing_comma_py.snap | 533 ++--- ...er__tests__black_test__composition_py.snap | 860 ------- ...sts__black_test__docstring_preview_py.snap | 49 +- ...tter__tests__black_test__docstring_py.snap | 255 +- ...er__tests__black_test__empty_lines_py.snap | 187 +- ...ter__tests__black_test__expression_py.snap | 728 +++--- ...tter__tests__black_test__fmtonoff2_py.snap | 205 -- ...tter__tests__black_test__fmtonoff3_py.snap | 41 +- ...tter__tests__black_test__fmtonoff4_py.snap | 70 +- ...tter__tests__black_test__fmtonoff5_py.snap | 138 +- ...atter__tests__black_test__fmtonoff_py.snap | 484 ++-- ...atter__tests__black_test__fmtskip2_py.snap | 45 +- ...atter__tests__black_test__fmtskip3_py.snap | 28 +- ...atter__tests__black_test__fmtskip4_py.snap | 51 + ...atter__tests__black_test__fmtskip5_py.snap | 26 +- ...atter__tests__black_test__fmtskip7_py.snap | 9 +- ...atter__tests__black_test__fmtskip8_py.snap | 293 --- ...tter__tests__black_test__function2_py.snap | 83 +- ...atter__tests__black_test__function_py.snap | 311 ++- ...lack_test__function_trailing_comma_py.snap | 230 +- ..._tests__black_test__import_spacing_py.snap | 242 ++ ..._black_test__one_element_subscript_py.snap | 98 + ...ests__black_test__power_op_spacing_py.snap | 230 ++ ...test__prefer_rhs_split_reformatted_py.snap | 96 + ...s__black_test__remove_await_parens_py.snap | 181 +- ...__black_test__remove_except_parens_py.snap | 26 +- ...s__black_test__remove_for_brackets_py.snap | 44 +- ...move_newline_after_code_block_open_py.snap | 428 ++++ ...__tests__black_test__remove_parens_py.snap | 145 +- ...k_test__return_annotation_brackets_py.snap | 293 ++- ...ck_test__skip_magic_trailing_comma_py.snap | 74 +- ...rmatter__tests__black_test__slices_py.snap | 223 ++ ...tests__black_test__string_prefixes_py.snap | 18 +- ...matter__tests__black_test__torture_py.snap | 165 +- ...t__trailing_comma_optional_parens1_py.snap | 78 +- ...t__trailing_comma_optional_parens2_py.snap | 14 +- ...t__trailing_comma_optional_parens3_py.snap | 60 - ...__trailing_commas_in_leading_parts_py.snap | 66 +- ...er__tests__black_test__tupleassign_py.snap | 27 +- crates/ruff_python_formatter/src/trivia.rs | 1031 -------- 85 files changed, 5297 insertions(+), 13234 deletions(-) delete mode 100644 crates/ruff_python_formatter/src/attachment.rs delete mode 100644 crates/ruff_python_formatter/src/cst/helpers.rs delete mode 100644 crates/ruff_python_formatter/src/cst/mod.rs delete mode 100644 crates/ruff_python_formatter/src/cst/visitor.rs delete mode 100644 crates/ruff_python_formatter/src/format/alias.rs delete mode 100644 crates/ruff_python_formatter/src/format/arg.rs delete mode 100644 crates/ruff_python_formatter/src/format/arguments.rs delete mode 100644 crates/ruff_python_formatter/src/format/bool_op.rs delete mode 100644 crates/ruff_python_formatter/src/format/builders.rs delete mode 100644 crates/ruff_python_formatter/src/format/cmp_op.rs delete mode 100644 crates/ruff_python_formatter/src/format/comments.rs delete mode 100644 crates/ruff_python_formatter/src/format/comprehension.rs delete mode 100644 crates/ruff_python_formatter/src/format/excepthandler.rs delete mode 100644 crates/ruff_python_formatter/src/format/expr.rs delete mode 100644 crates/ruff_python_formatter/src/format/helpers.rs delete mode 100644 crates/ruff_python_formatter/src/format/keyword.rs delete mode 100644 crates/ruff_python_formatter/src/format/match_case.rs delete mode 100644 crates/ruff_python_formatter/src/format/mod.rs delete mode 100644 crates/ruff_python_formatter/src/format/numbers.rs delete mode 100644 crates/ruff_python_formatter/src/format/operator.rs delete mode 100644 crates/ruff_python_formatter/src/format/pattern.rs delete mode 100644 crates/ruff_python_formatter/src/format/stmt.rs delete mode 100644 crates/ruff_python_formatter/src/format/strings.rs delete mode 100644 crates/ruff_python_formatter/src/format/unary_op.rs delete mode 100644 crates/ruff_python_formatter/src/format/withitem.rs create mode 100644 crates/ruff_python_formatter/src/module/mod.rs delete mode 100644 crates/ruff_python_formatter/src/newlines.rs delete mode 100644 crates/ruff_python_formatter/src/parentheses.rs create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__attribute_access_on_number_literals_py.snap rename crates/ruff_python_formatter/src/snapshots/{ruff_python_formatter__tests__black_test__fmtskip_py.snap => ruff_python_formatter__tests__black_test__beginning_backslash_py.snap} (54%) create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__bracketmatch_py.snap create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__class_blank_parentheses_py.snap create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__class_methods_new_line_py.snap create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__collections_py.snap create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comment_after_escaped_newline_py.snap delete mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments3_py.snap delete mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments4_py.snap delete mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments5_py.snap delete mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments6_py.snap create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments8_py.snap delete mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__composition_py.snap delete mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff2_py.snap create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip4_py.snap delete mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip8_py.snap create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__import_spacing_py.snap create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__one_element_subscript_py.snap create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__power_op_spacing_py.snap create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__prefer_rhs_split_reformatted_py.snap create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_newline_after_code_block_open_py.snap create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__slices_py.snap delete mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens3_py.snap delete mode 100644 crates/ruff_python_formatter/src/trivia.rs diff --git a/crates/ruff_python_formatter/src/attachment.rs b/crates/ruff_python_formatter/src/attachment.rs deleted file mode 100644 index e738c460a9e23..0000000000000 --- a/crates/ruff_python_formatter/src/attachment.rs +++ /dev/null @@ -1,125 +0,0 @@ -use crate::cst::visitor; -use crate::cst::visitor::Visitor; -use crate::cst::{ - Alias, Arg, Body, BoolOp, CmpOp, Excepthandler, Expr, Keyword, Operator, Pattern, SliceIndex, - Stmt, UnaryOp, -}; -use crate::trivia::{decorate_trivia, TriviaIndex, TriviaToken}; - -struct AttachmentVisitor { - index: TriviaIndex, -} - -impl<'a> Visitor<'a> for AttachmentVisitor { - fn visit_body(&mut self, body: &'a mut Body) { - let trivia = self.index.body.remove(&body.id()); - if let Some(comments) = trivia { - body.trivia.extend(comments); - } - visitor::walk_body(self, body); - } - - fn visit_stmt(&mut self, stmt: &'a mut Stmt) { - let trivia = self.index.stmt.remove(&stmt.id()); - if let Some(comments) = trivia { - stmt.trivia.extend(comments); - } - visitor::walk_stmt(self, stmt); - } - - fn visit_expr(&mut self, expr: &'a mut Expr) { - let trivia = self.index.expr.remove(&expr.id()); - if let Some(comments) = trivia { - expr.trivia.extend(comments); - } - visitor::walk_expr(self, expr); - } - - fn visit_alias(&mut self, alias: &'a mut Alias) { - let trivia = self.index.alias.remove(&alias.id()); - if let Some(comments) = trivia { - alias.trivia.extend(comments); - } - visitor::walk_alias(self, alias); - } - - fn visit_arg(&mut self, arg: &'a mut Arg) { - let trivia = self.index.arg.remove(&arg.id()); - if let Some(comments) = trivia { - arg.trivia.extend(comments); - } - visitor::walk_arg(self, arg); - } - - fn visit_excepthandler(&mut self, excepthandler: &'a mut Excepthandler) { - let trivia = self.index.excepthandler.remove(&excepthandler.id()); - if let Some(comments) = trivia { - excepthandler.trivia.extend(comments); - } - visitor::walk_excepthandler(self, excepthandler); - } - - fn visit_keyword(&mut self, keyword: &'a mut Keyword) { - let trivia = self.index.keyword.remove(&keyword.id()); - if let Some(comments) = trivia { - keyword.trivia.extend(comments); - } - visitor::walk_keyword(self, keyword); - } - - fn visit_bool_op(&mut self, bool_op: &'a mut BoolOp) { - let trivia = self.index.bool_op.remove(&bool_op.id()); - if let Some(comments) = trivia { - bool_op.trivia.extend(comments); - } - visitor::walk_bool_op(self, bool_op); - } - - fn visit_unary_op(&mut self, unary_op: &'a mut UnaryOp) { - let trivia = self.index.unary_op.remove(&unary_op.id()); - if let Some(comments) = trivia { - unary_op.trivia.extend(comments); - } - visitor::walk_unary_op(self, unary_op); - } - - fn visit_cmp_op(&mut self, cmp_op: &'a mut CmpOp) { - let trivia = self.index.cmp_op.remove(&cmp_op.id()); - if let Some(comments) = trivia { - cmp_op.trivia.extend(comments); - } - visitor::walk_cmp_op(self, cmp_op); - } - - fn visit_operator(&mut self, operator: &'a mut Operator) { - let trivia = self.index.operator.remove(&operator.id()); - if let Some(comments) = trivia { - operator.trivia.extend(comments); - } - visitor::walk_operator(self, operator); - } - - fn visit_slice_index(&mut self, slice_index: &'a mut SliceIndex) { - let trivia = self.index.slice_index.remove(&slice_index.id()); - if let Some(comments) = trivia { - slice_index.trivia.extend(comments); - } - visitor::walk_slice_index(self, slice_index); - } - - fn visit_pattern(&mut self, pattern: &'a mut Pattern) { - let trivia = self.index.pattern.remove(&pattern.id()); - if let Some(comments) = trivia { - pattern.trivia.extend(comments); - } - visitor::walk_pattern(self, pattern); - } -} - -pub(crate) fn attach(python_cst: &mut [Stmt], trivia: Vec) { - let index = decorate_trivia(trivia, python_cst); - let mut visitor = AttachmentVisitor { index }; - for stmt in python_cst { - visitor.visit_stmt(stmt); - } -} diff --git a/crates/ruff_python_formatter/src/cst/helpers.rs b/crates/ruff_python_formatter/src/cst/helpers.rs deleted file mode 100644 index 4c025f9bc090e..0000000000000 --- a/crates/ruff_python_formatter/src/cst/helpers.rs +++ /dev/null @@ -1,138 +0,0 @@ -use ruff_python_ast::source_code::Locator; -use ruff_text_size::{TextLen, TextRange, TextSize}; -use rustpython_parser::ast::Ranged; - -/// Return `true` if the given string is a radix literal (e.g., `0b101`). -pub(crate) fn is_radix_literal(content: &str) -> bool { - content.starts_with("0b") - || content.starts_with("0o") - || content.starts_with("0x") - || content.starts_with("0B") - || content.starts_with("0O") - || content.starts_with("0X") -} - -/// Find the first token in the given range that satisfies the given predicate. -pub(crate) fn find_tok( - range: TextRange, - locator: &Locator, - f: impl Fn(rustpython_parser::Tok) -> bool, -) -> TextRange { - for (tok, tok_range) in rustpython_parser::lexer::lex_starts_at( - &locator.contents()[range], - rustpython_parser::Mode::Module, - range.start(), - ) - .flatten() - { - if f(tok) { - return tok_range; - } - } - unreachable!("Failed to find token in range {:?}", range) -} - -/// Expand the range of a compound statement. -/// -/// `location` is the start of the compound statement (e.g., the `if` in `if x:`). -/// `end_location` is the end of the last statement in the body. -pub(crate) fn expand_indented_block( - location: TextSize, - end_location: TextSize, - locator: &Locator, -) -> TextRange { - let contents = locator.contents(); - - // Find the colon, which indicates the end of the header. - let mut nesting = 0; - let mut colon = None; - for (tok, tok_range) in rustpython_parser::lexer::lex_starts_at( - &contents[TextRange::new(location, end_location)], - rustpython_parser::Mode::Module, - location, - ) - .flatten() - { - match tok { - rustpython_parser::Tok::Colon if nesting == 0 => { - colon = Some(tok_range.start()); - break; - } - rustpython_parser::Tok::Lpar - | rustpython_parser::Tok::Lsqb - | rustpython_parser::Tok::Lbrace => nesting += 1, - rustpython_parser::Tok::Rpar - | rustpython_parser::Tok::Rsqb - | rustpython_parser::Tok::Rbrace => nesting -= 1, - _ => {} - } - } - let colon_location = colon.unwrap(); - - // From here, we have two options: simple statement or compound statement. - let indent = rustpython_parser::lexer::lex_starts_at( - &contents[TextRange::new(colon_location, end_location)], - rustpython_parser::Mode::Module, - colon_location, - ) - .flatten() - .find_map(|(tok, range)| match tok { - rustpython_parser::Tok::Indent => Some(range.end()), - _ => None, - }); - - let line_end = locator.line_end(end_location); - let Some(indent_end) = indent else { - - // Simple statement: from the colon to the end of the line. - return TextRange::new(colon_location, line_end); - }; - - let indent_width = indent_end - locator.line_start(indent_end); - - // Compound statement: from the colon to the end of the block. - // For each line that follows, check that there's no content up to the expected indent. - let mut offset = TextSize::default(); - let mut line_offset = TextSize::default(); - // Issue, body goes to far.. it includes the whole try including the catch - - let rest = &contents[usize::from(line_end)..]; - for (relative_offset, c) in rest.char_indices() { - if line_offset < indent_width && !c.is_whitespace() { - break; // Found end of block - } - - match c { - '\n' | '\r' => { - // Ignore empty lines - if line_offset > TextSize::from(0) { - offset = TextSize::try_from(relative_offset).unwrap() + TextSize::from(1); - } - line_offset = TextSize::from(0); - } - _ => { - line_offset += c.text_len(); - } - } - } - - // Reached end of file - let end = if line_offset >= indent_width { - contents.text_len() - } else { - line_end + offset - }; - - TextRange::new(colon_location, end) -} - -/// Return true if the `orelse` block of an `if` statement is an `elif` statement. -pub(crate) fn is_elif(orelse: &[rustpython_parser::ast::Stmt], locator: &Locator) -> bool { - if orelse.len() == 1 && matches!(orelse[0], rustpython_parser::ast::Stmt::If { .. }) { - let contents = locator.after(orelse[0].start()); - if contents.starts_with("elif") { - return true; - } - } - false -} diff --git a/crates/ruff_python_formatter/src/cst/mod.rs b/crates/ruff_python_formatter/src/cst/mod.rs deleted file mode 100644 index 239629bdd0cfd..0000000000000 --- a/crates/ruff_python_formatter/src/cst/mod.rs +++ /dev/null @@ -1,2123 +0,0 @@ -#![allow(clippy::derive_partial_eq_without_eq)] - -use std::iter; -use std::ops::Deref; - -use itertools::Itertools; -use ruff_text_size::{TextRange, TextSize}; -use rustpython_parser::ast::{Constant, ConversionFlag, Ranged}; -use rustpython_parser::{ast, Mode}; - -use ruff_python_ast::source_code::Locator; - -use crate::cst::helpers::{expand_indented_block, find_tok, is_elif}; -use crate::trivia::{Parenthesize, Trivia}; - -pub(crate) mod helpers; -pub(crate) mod visitor; - -type Ident = String; - -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct Attributed { - pub(crate) range: TextRange, - pub(crate) node: T, - pub(crate) trivia: Vec, - pub(crate) parentheses: Parenthesize, -} - -impl Attributed { - pub(crate) fn new(range: TextRange, node: T) -> Self { - Self { - range, - node, - trivia: Vec::new(), - parentheses: Parenthesize::Never, - } - } - - pub(crate) const fn range(&self) -> TextRange { - self.range - } - - pub(crate) const fn start(&self) -> TextSize { - self.range.start() - } - - pub(crate) const fn end(&self) -> TextSize { - self.range.end() - } - - pub(crate) fn id(&self) -> usize { - std::ptr::addr_of!(self.node) as usize - } -} - -impl Deref for Attributed { - type Target = T; - fn deref(&self) -> &Self::Target { - &self.node - } -} - -#[derive(Clone, Debug, PartialEq)] -pub(crate) enum ExprContext { - Load, - Store, - Del, -} - -impl From for ExprContext { - fn from(context: ast::ExprContext) -> Self { - match context { - ast::ExprContext::Load => Self::Load, - ast::ExprContext::Store => Self::Store, - ast::ExprContext::Del => Self::Del, - } - } -} - -#[derive(Clone, Debug, PartialEq)] -pub(crate) enum BoolOpKind { - And, - Or, -} - -impl From<&ast::Boolop> for BoolOpKind { - fn from(op: &ast::Boolop) -> Self { - match op { - ast::Boolop::And => Self::And, - ast::Boolop::Or => Self::Or, - } - } -} - -pub(crate) type BoolOp = Attributed; - -#[derive(Clone, Debug, PartialEq)] -pub(crate) enum OperatorKind { - Add, - Sub, - Mult, - MatMult, - Div, - Mod, - Pow, - LShift, - RShift, - BitOr, - BitXor, - BitAnd, - FloorDiv, -} - -pub(crate) type Operator = Attributed; - -impl From<&ast::Operator> for OperatorKind { - fn from(op: &ast::Operator) -> Self { - match op { - ast::Operator::Add => Self::Add, - ast::Operator::Sub => Self::Sub, - ast::Operator::Mult => Self::Mult, - ast::Operator::MatMult => Self::MatMult, - ast::Operator::Div => Self::Div, - ast::Operator::Mod => Self::Mod, - ast::Operator::Pow => Self::Pow, - ast::Operator::LShift => Self::LShift, - ast::Operator::RShift => Self::RShift, - ast::Operator::BitOr => Self::BitOr, - ast::Operator::BitXor => Self::BitXor, - ast::Operator::BitAnd => Self::BitAnd, - ast::Operator::FloorDiv => Self::FloorDiv, - } - } -} - -#[derive(Clone, Debug, PartialEq)] -pub(crate) enum UnaryOpKind { - Invert, - Not, - UAdd, - USub, -} - -pub(crate) type UnaryOp = Attributed; - -impl From<&ast::Unaryop> for UnaryOpKind { - fn from(op: &ast::Unaryop) -> Self { - match op { - ast::Unaryop::Invert => Self::Invert, - ast::Unaryop::Not => Self::Not, - ast::Unaryop::UAdd => Self::UAdd, - ast::Unaryop::USub => Self::USub, - } - } -} - -#[derive(Clone, Debug, PartialEq)] -pub(crate) enum CmpOpKind { - Eq, - NotEq, - Lt, - LtE, - Gt, - GtE, - Is, - IsNot, - In, - NotIn, -} - -pub(crate) type CmpOp = Attributed; - -impl From<&ast::Cmpop> for CmpOpKind { - fn from(op: &ast::Cmpop) -> Self { - match op { - ast::Cmpop::Eq => Self::Eq, - ast::Cmpop::NotEq => Self::NotEq, - ast::Cmpop::Lt => Self::Lt, - ast::Cmpop::LtE => Self::LtE, - ast::Cmpop::Gt => Self::Gt, - ast::Cmpop::GtE => Self::GtE, - ast::Cmpop::Is => Self::Is, - ast::Cmpop::IsNot => Self::IsNot, - ast::Cmpop::In => Self::In, - ast::Cmpop::NotIn => Self::NotIn, - } - } -} - -pub(crate) type Body = Attributed>; - -impl From<(Vec, &Locator<'_>)> for Body { - fn from((body, locator): (Vec, &Locator)) -> Self { - Body { - range: body.first().unwrap().range(), - node: body - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - } -} - -#[derive(Clone, Debug, PartialEq)] -pub(crate) enum StmtKind { - FunctionDef { - name: Ident, - args: Box, - body: Body, - decorator_list: Vec, - returns: Option>, - type_comment: Option, - }, - AsyncFunctionDef { - name: Ident, - args: Box, - body: Body, - decorator_list: Vec, - returns: Option>, - type_comment: Option, - }, - ClassDef { - name: Ident, - bases: Vec, - keywords: Vec, - body: Body, - decorator_list: Vec, - }, - Return { - value: Option, - }, - Delete { - targets: Vec, - }, - Assign { - targets: Vec, - value: Box, - type_comment: Option, - }, - AugAssign { - target: Box, - op: Operator, - value: Box, - }, - AnnAssign { - target: Box, - annotation: Box, - value: Option>, - simple: usize, - }, - For { - target: Box, - iter: Box, - body: Body, - orelse: Option, - type_comment: Option, - }, - AsyncFor { - target: Box, - iter: Box, - body: Body, - orelse: Option, - type_comment: Option, - }, - While { - test: Box, - body: Body, - orelse: Option, - }, - If { - test: Box, - body: Body, - orelse: Option, - is_elif: bool, - }, - With { - items: Vec, - body: Body, - type_comment: Option, - }, - AsyncWith { - items: Vec, - body: Body, - type_comment: Option, - }, - Match { - subject: Box, - cases: Vec, - }, - Raise { - exc: Option>, - cause: Option>, - }, - Try { - body: Body, - handlers: Vec, - orelse: Option, - finalbody: Option, - }, - TryStar { - body: Body, - handlers: Vec, - orelse: Option, - finalbody: Option, - }, - Assert { - test: Box, - msg: Option>, - }, - Import { - names: Vec, - }, - ImportFrom { - module: Option, - names: Vec, - level: Option, - }, - Global { - names: Vec, - }, - Nonlocal { - names: Vec, - }, - Expr { - value: Box, - }, - Pass, - Break, - Continue, -} - -pub(crate) type Stmt = Attributed; - -#[derive(Clone, Debug, PartialEq)] -pub(crate) enum ExprKind { - BoolOp { - ops: Vec, - values: Vec, - }, - NamedExpr { - target: Box, - value: Box, - }, - BinOp { - left: Box, - op: Operator, - right: Box, - }, - UnaryOp { - op: UnaryOp, - operand: Box, - }, - Lambda { - args: Box, - body: Box, - }, - IfExp { - test: Box, - body: Box, - orelse: Box, - }, - Dict { - keys: Vec>, - values: Vec, - }, - Set { - elts: Vec, - }, - ListComp { - elt: Box, - generators: Vec, - }, - SetComp { - elt: Box, - generators: Vec, - }, - DictComp { - key: Box, - value: Box, - generators: Vec, - }, - GeneratorExp { - elt: Box, - generators: Vec, - }, - Await { - value: Box, - }, - Yield { - value: Option>, - }, - YieldFrom { - value: Box, - }, - Compare { - left: Box, - ops: Vec, - comparators: Vec, - }, - Call { - func: Box, - args: Vec, - keywords: Vec, - }, - FormattedValue { - value: Box, - conversion: ConversionFlag, - format_spec: Option>, - }, - JoinedStr { - values: Vec, - }, - Constant { - value: Constant, - kind: Option, - }, - Attribute { - value: Box, - attr: Ident, - ctx: ExprContext, - }, - Subscript { - value: Box, - slice: Box, - ctx: ExprContext, - }, - Starred { - value: Box, - ctx: ExprContext, - }, - Name { - id: String, - ctx: ExprContext, - }, - List { - elts: Vec, - ctx: ExprContext, - }, - Tuple { - elts: Vec, - ctx: ExprContext, - }, - Slice { - lower: SliceIndex, - upper: SliceIndex, - step: Option, - }, -} - -pub(crate) type Expr = Attributed; - -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct Comprehension { - pub(crate) target: Expr, - pub(crate) iter: Expr, - pub(crate) ifs: Vec, - pub(crate) is_async: usize, -} - -#[derive(Clone, Debug, PartialEq)] -pub(crate) enum ExcepthandlerKind { - ExceptHandler { - type_: Option>, - name: Option, - body: Body, - }, -} - -pub(crate) type Excepthandler = Attributed; - -#[derive(Clone, Debug, PartialEq)] -pub(crate) enum SliceIndexKind { - /// The index slot exists, but is empty. - Empty, - /// The index slot contains an expression. - Index { value: Box }, -} - -pub(crate) type SliceIndex = Attributed; - -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct Arguments { - pub(crate) posonlyargs: Vec, - pub(crate) args: Vec, - pub(crate) vararg: Option>, - pub(crate) kwonlyargs: Vec, - pub(crate) kw_defaults: Vec, - pub(crate) kwarg: Option>, - pub(crate) defaults: Vec, -} - -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct ArgData { - pub(crate) arg: Ident, - pub(crate) annotation: Option>, - pub(crate) type_comment: Option, -} - -pub(crate) type Arg = Attributed; - -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct KeywordData { - pub(crate) arg: Option, - pub(crate) value: Expr, -} - -pub(crate) type Keyword = Attributed; - -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct AliasData { - pub(crate) name: Ident, - pub(crate) asname: Option, -} - -pub(crate) type Alias = Attributed; - -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct Withitem { - pub(crate) context_expr: Expr, - pub(crate) optional_vars: Option>, -} - -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct MatchCase { - pub(crate) pattern: Pattern, - pub(crate) guard: Option>, - pub(crate) body: Body, -} - -#[allow(clippy::enum_variant_names)] -#[derive(Clone, Debug, PartialEq)] -pub(crate) enum PatternKind { - MatchValue { - value: Box, - }, - MatchSingleton { - value: Constant, - }, - MatchSequence { - patterns: Vec, - }, - MatchMapping { - keys: Vec, - patterns: Vec, - rest: Option, - }, - MatchClass { - cls: Box, - patterns: Vec, - kwd_attrs: Vec, - kwd_patterns: Vec, - }, - MatchStar { - name: Option, - }, - MatchAs { - pattern: Option>, - name: Option, - }, - MatchOr { - patterns: Vec, - }, -} - -pub(crate) type Pattern = Attributed; - -impl From<(ast::Alias, &Locator<'_>)> for Alias { - fn from((alias, _locator): (ast::Alias, &Locator)) -> Self { - Alias { - range: alias.range(), - node: AliasData { - name: alias.name.to_string(), - asname: alias.asname.as_ref().map(ast::Identifier::to_string), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - } - } -} - -impl From<(ast::Withitem, &Locator<'_>)> for Withitem { - fn from((withitem, locator): (ast::Withitem, &Locator)) -> Self { - Withitem { - context_expr: (withitem.context_expr, locator).into(), - optional_vars: withitem - .optional_vars - .map(|v| Box::new((*v, locator).into())), - } - } -} - -impl From<(ast::Excepthandler, &Locator<'_>)> for Excepthandler { - fn from((excepthandler, locator): (ast::Excepthandler, &Locator)) -> Self { - let ast::Excepthandler::ExceptHandler(ast::ExcepthandlerExceptHandler { - type_, - name, - body, - range, - }) = excepthandler; - - // Find the start and end of the `body`. - let body = { - let body_range = - expand_indented_block(range.start(), body.last().unwrap().end(), locator); - Body { - range: body_range, - node: body - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }; - - Excepthandler { - range: TextRange::new(range.start(), body.end()), - node: ExcepthandlerKind::ExceptHandler { - type_: type_.map(|type_| Box::new((*type_, locator).into())), - name: name.map(Into::into), - body, - }, - trivia: vec![], - parentheses: Parenthesize::Never, - } - } -} - -impl From<(ast::Pattern, &Locator<'_>)> for Pattern { - fn from((pattern, locator): (ast::Pattern, &Locator)) -> Self { - Pattern { - range: pattern.range(), - node: match pattern { - ast::Pattern::MatchValue(ast::PatternMatchValue { value, range: _ }) => { - PatternKind::MatchValue { - value: Box::new((*value, locator).into()), - } - } - ast::Pattern::MatchSingleton(ast::PatternMatchSingleton { value, range: _ }) => { - PatternKind::MatchSingleton { value } - } - ast::Pattern::MatchSequence(ast::PatternMatchSequence { patterns, range: _ }) => { - PatternKind::MatchSequence { - patterns: patterns - .into_iter() - .map(|pattern| (pattern, locator).into()) - .collect(), - } - } - ast::Pattern::MatchMapping(ast::PatternMatchMapping { - keys, - patterns, - rest, - range: _, - }) => PatternKind::MatchMapping { - keys: keys.into_iter().map(|key| (key, locator).into()).collect(), - patterns: patterns - .into_iter() - .map(|pattern| (pattern, locator).into()) - .collect(), - rest: rest.map(Into::into), - }, - ast::Pattern::MatchClass(ast::PatternMatchClass { - cls, - patterns, - kwd_attrs, - kwd_patterns, - range: _, - }) => PatternKind::MatchClass { - cls: Box::new((*cls, locator).into()), - patterns: patterns - .into_iter() - .map(|pattern| (pattern, locator).into()) - .collect(), - kwd_attrs: kwd_attrs.into_iter().map(Into::into).collect(), - kwd_patterns: kwd_patterns - .into_iter() - .map(|pattern| (pattern, locator).into()) - .collect(), - }, - ast::Pattern::MatchStar(ast::PatternMatchStar { name, range: _ }) => { - PatternKind::MatchStar { - name: name.map(Into::into), - } - } - ast::Pattern::MatchAs(ast::PatternMatchAs { - pattern, - name, - range: _, - }) => PatternKind::MatchAs { - pattern: pattern.map(|pattern| Box::new((*pattern, locator).into())), - name: name.map(Into::into), - }, - ast::Pattern::MatchOr(ast::PatternMatchOr { patterns, range: _ }) => { - PatternKind::MatchOr { - patterns: patterns - .into_iter() - .map(|pattern| (pattern, locator).into()) - .collect(), - } - } - }, - trivia: vec![], - parentheses: Parenthesize::Never, - } - } -} - -impl From<(ast::MatchCase, &Locator<'_>)> for MatchCase { - fn from((match_case, locator): (ast::MatchCase, &Locator)) -> Self { - // Find the start and end of the `body`. - let body = { - let body_range = expand_indented_block( - match_case.pattern.start(), - match_case.body.last().unwrap().end(), - locator, - ); - Body { - range: body_range, - node: match_case - .body - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }; - - MatchCase { - pattern: (match_case.pattern, locator).into(), - guard: match_case - .guard - .map(|guard| Box::new((*guard, locator).into())), - body, - } - } -} - -impl From<(ast::Stmt, &Locator<'_>)> for Stmt { - fn from((stmt, locator): (ast::Stmt, &Locator)) -> Self { - match stmt { - ast::Stmt::Expr(ast::StmtExpr { value, range }) => Stmt { - range, - node: StmtKind::Expr { - value: Box::new((*value, locator).into()), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Stmt::Pass(ast::StmtPass { range }) => Stmt { - range, - node: StmtKind::Pass, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Stmt::Return(ast::StmtReturn { value, range }) => Stmt { - range, - node: StmtKind::Return { - value: value.map(|v| (*v, locator).into()), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Stmt::Assign(ast::StmtAssign { - targets, - value, - type_comment, - range, - }) => Stmt { - range, - node: StmtKind::Assign { - targets: targets - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - value: Box::new((*value, locator).into()), - type_comment, - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Stmt::ClassDef(ast::StmtClassDef { - name, - bases, - keywords, - body, - decorator_list, - range, - }) => { - // Find the start and end of the `body`. - let body = { - let body_range = - expand_indented_block(range.start(), body.last().unwrap().end(), locator); - Body { - range: body_range, - node: body - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }; - - Stmt { - range: TextRange::new(range.start(), body.end()), - node: StmtKind::ClassDef { - name: name.into(), - bases: bases - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - keywords: keywords - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - body, - decorator_list: decorator_list - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - } - } - ast::Stmt::If(ast::StmtIf { - test, - body, - orelse, - range, - }) => { - // Find the start and end of the `body`. - let body = { - let body_range = - expand_indented_block(range.start(), body.last().unwrap().end(), locator); - Body { - range: body_range, - node: body - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }; - - if orelse.is_empty() { - // No `else` block. - Stmt { - range: TextRange::new(range.start(), body.end()), - node: StmtKind::If { - test: Box::new((*test, locator).into()), - body, - orelse: None, - is_elif: false, - }, - trivia: vec![], - parentheses: Parenthesize::Never, - } - } else { - if is_elif(&orelse, locator) { - // Find the start and end of the `elif`. - let mut elif: Body = (orelse, locator).into(); - if let Attributed { - node: StmtKind::If { is_elif, .. }, - .. - } = elif.node.first_mut().unwrap() - { - *is_elif = true; - }; - - Stmt { - range: TextRange::new(range.start(), elif.end()), - node: StmtKind::If { - test: Box::new((*test, locator).into()), - body, - orelse: Some(elif), - is_elif: false, - }, - trivia: vec![], - parentheses: Parenthesize::Never, - } - } else { - // Find the start and end of the `else`. - let orelse_range = expand_indented_block( - body.end(), - orelse.last().unwrap().end(), - locator, - ); - let orelse = Body { - range: orelse_range, - node: orelse - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - }; - - Stmt { - range: TextRange::new(range.start(), orelse.end()), - node: StmtKind::If { - test: Box::new((*test, locator).into()), - body, - orelse: Some(orelse), - is_elif: false, - }, - trivia: vec![], - parentheses: Parenthesize::Never, - } - } - } - } - ast::Stmt::Assert(ast::StmtAssert { test, msg, range }) => Stmt { - range, - node: StmtKind::Assert { - test: Box::new((*test, locator).into()), - msg: msg.map(|node| Box::new((*node, locator).into())), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Stmt::FunctionDef(ast::StmtFunctionDef { - name, - args, - body, - decorator_list, - returns, - type_comment, - range, - }) => { - // Find the start and end of the `body`. - let body = { - let body_range = - expand_indented_block(range.start(), body.last().unwrap().end(), locator); - Body { - range: body_range, - node: body - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }; - - Stmt { - range: TextRange::new(range.start(), body.end()), - node: StmtKind::FunctionDef { - name: name.into(), - args: Box::new((*args, locator).into()), - body, - decorator_list: decorator_list - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - returns: returns.map(|r| Box::new((*r, locator).into())), - type_comment, - }, - trivia: vec![], - parentheses: Parenthesize::Never, - } - } - ast::Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef { - name, - args, - body, - decorator_list, - returns, - type_comment, - range, - }) => { - // Find the start and end of the `body`. - let body = { - let body_range = - expand_indented_block(range.start(), body.last().unwrap().end(), locator); - Body { - range: body_range, - node: body - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }; - - Stmt { - range: TextRange::new(range.start(), body.end()), - node: StmtKind::AsyncFunctionDef { - name: name.into(), - args: Box::new((*args, locator).into()), - body, - decorator_list: decorator_list - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - returns: returns.map(|r| Box::new((*r, locator).into())), - type_comment, - }, - trivia: vec![], - parentheses: Parenthesize::Never, - } - } - ast::Stmt::Delete(ast::StmtDelete { targets, range }) => Stmt { - range, - node: StmtKind::Delete { - targets: targets - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Stmt::AugAssign(ast::StmtAugAssign { - target, - op, - value, - range, - }) => Stmt { - range, - node: StmtKind::AugAssign { - op: { - let target_tok = match &op { - ast::Operator::Add => rustpython_parser::Tok::PlusEqual, - ast::Operator::Sub => rustpython_parser::Tok::MinusEqual, - ast::Operator::Mult => rustpython_parser::Tok::StarEqual, - ast::Operator::MatMult => rustpython_parser::Tok::AtEqual, - ast::Operator::Div => rustpython_parser::Tok::SlashEqual, - ast::Operator::Mod => rustpython_parser::Tok::PercentEqual, - ast::Operator::Pow => rustpython_parser::Tok::DoubleStarEqual, - ast::Operator::LShift => rustpython_parser::Tok::LeftShiftEqual, - ast::Operator::RShift => rustpython_parser::Tok::RightShiftEqual, - ast::Operator::BitOr => rustpython_parser::Tok::VbarEqual, - ast::Operator::BitXor => rustpython_parser::Tok::CircumflexEqual, - ast::Operator::BitAnd => rustpython_parser::Tok::AmperEqual, - ast::Operator::FloorDiv => rustpython_parser::Tok::DoubleSlashEqual, - }; - let op_range = - find_tok(TextRange::new(target.end(), value.end()), locator, |tok| { - tok == target_tok - }); - Operator::new(op_range, (&op).into()) - }, - target: Box::new((*target, locator).into()), - value: Box::new((*value, locator).into()), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Stmt::AnnAssign(ast::StmtAnnAssign { - target, - annotation, - value, - simple, - range, - }) => Stmt { - range, - node: StmtKind::AnnAssign { - target: Box::new((*target, locator).into()), - annotation: Box::new((*annotation, locator).into()), - value: value.map(|node| Box::new((*node, locator).into())), - simple: usize::from(simple), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Stmt::For(ast::StmtFor { - target, - iter, - body, - orelse, - type_comment, - range, - }) => { - // Find the start and end of the `body`. - let body = { - let body_range = - expand_indented_block(range.start(), body.last().unwrap().end(), locator); - Body { - range: body_range, - node: body - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }; - - // Find the start and end of the `orelse`. - let orelse = (!orelse.is_empty()).then(|| { - let orelse_range = - expand_indented_block(body.end(), orelse.last().unwrap().end(), locator); - Body { - range: orelse_range, - node: orelse - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }); - - Stmt { - range: TextRange::new(range.start(), orelse.as_ref().unwrap_or(&body).end()), - node: StmtKind::For { - target: Box::new((*target, locator).into()), - iter: Box::new((*iter, locator).into()), - body, - orelse, - type_comment, - }, - trivia: vec![], - parentheses: Parenthesize::Never, - } - } - ast::Stmt::AsyncFor(ast::StmtAsyncFor { - target, - iter, - body, - orelse, - type_comment, - range, - }) => { - // Find the start and end of the `body`. - let body = { - let body_range = - expand_indented_block(range.start(), body.last().unwrap().end(), locator); - Body { - range: body_range, - node: body - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }; - - // Find the start and end of the `orelse`. - let orelse = (!orelse.is_empty()).then(|| { - let orelse_range = - expand_indented_block(body.end(), orelse.last().unwrap().end(), locator); - Body { - range: orelse_range, - node: orelse - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }); - - Stmt { - range: TextRange::new(range.start(), orelse.as_ref().unwrap_or(&body).end()), - node: StmtKind::AsyncFor { - target: Box::new((*target, locator).into()), - iter: Box::new((*iter, locator).into()), - body, - orelse, - type_comment, - }, - trivia: vec![], - parentheses: Parenthesize::Never, - } - } - ast::Stmt::While(ast::StmtWhile { - test, - body, - orelse, - range, - }) => { - // Find the start and end of the `body`. - let body = { - let body_range = - expand_indented_block(range.start(), body.last().unwrap().end(), locator); - Body { - range: body_range, - node: body - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }; - - // Find the start and end of the `orelse`. - let orelse = (!orelse.is_empty()).then(|| { - let orelse_range = - expand_indented_block(body.end(), orelse.last().unwrap().end(), locator); - Body { - range: orelse_range, - node: orelse - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }); - - Stmt { - range: TextRange::new(range.start(), orelse.as_ref().unwrap_or(&body).end()), - node: StmtKind::While { - test: Box::new((*test, locator).into()), - body, - orelse, - }, - trivia: vec![], - parentheses: Parenthesize::Never, - } - } - ast::Stmt::With(ast::StmtWith { - items, - body, - type_comment, - range, - }) => { - // Find the start and end of the `body`. - let body = { - let body_range = - expand_indented_block(range.start(), body.last().unwrap().end(), locator); - Body { - range: body_range, - node: body - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }; - - Stmt { - range: TextRange::new(range.start(), body.end()), - node: StmtKind::With { - items: items - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - body, - type_comment, - }, - trivia: vec![], - parentheses: Parenthesize::Never, - } - } - ast::Stmt::AsyncWith(ast::StmtAsyncWith { - items, - body, - type_comment, - range, - }) => { - // Find the start and end of the `body`. - let body = { - let body_range = - expand_indented_block(range.start(), body.last().unwrap().end(), locator); - Body { - range: body_range, - node: body - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }; - - Stmt { - range: TextRange::new(range.start(), body.end()), - node: StmtKind::AsyncWith { - items: items - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - body, - type_comment, - }, - trivia: vec![], - parentheses: Parenthesize::Never, - } - } - ast::Stmt::Match(ast::StmtMatch { - subject, - cases, - range, - }) => Stmt { - range, - node: StmtKind::Match { - subject: Box::new((*subject, locator).into()), - cases: cases - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Stmt::Raise(ast::StmtRaise { exc, cause, range }) => Stmt { - range, - node: StmtKind::Raise { - exc: exc.map(|exc| Box::new((*exc, locator).into())), - cause: cause.map(|cause| Box::new((*cause, locator).into())), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Stmt::Try(ast::StmtTry { - body, - handlers, - orelse, - finalbody, - range, - }) => { - // Find the start and end of the `body`. - let body = { - let body_range = - expand_indented_block(range.start(), body.last().unwrap().end(), locator); - Body { - range: body_range, - node: body - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }; - - let handlers: Vec = handlers - .into_iter() - .map(|node| (node, locator).into()) - .collect(); - - // Find the start and end of the `orelse`. - let orelse = (!orelse.is_empty()).then(|| { - let orelse_range = expand_indented_block( - handlers.last().map_or(body.end(), Attributed::end), - orelse.last().unwrap().end(), - locator, - ); - Body { - range: orelse_range, - node: orelse - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }); - - // Find the start and end of the `finalbody`. - let finalbody = (!finalbody.is_empty()).then(|| { - let finalbody_range = expand_indented_block( - orelse.as_ref().map_or( - handlers.last().map_or(body.end(), Attributed::end), - Attributed::end, - ), - finalbody.last().unwrap().end(), - locator, - ); - Body { - range: finalbody_range, - node: finalbody - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }); - - let end_location = finalbody.as_ref().map_or( - orelse.as_ref().map_or( - handlers.last().map_or(body.end(), Attributed::end), - Attributed::end, - ), - Attributed::end, - ); - - Stmt { - range: TextRange::new(range.start(), end_location), - node: StmtKind::Try { - body, - handlers, - orelse, - finalbody, - }, - trivia: vec![], - parentheses: Parenthesize::Never, - } - } - ast::Stmt::TryStar(ast::StmtTryStar { - body, - handlers, - orelse, - finalbody, - range, - }) => { - // Find the start and end of the `body`. - let body = { - let body_range = - expand_indented_block(range.start(), body.last().unwrap().end(), locator); - Body { - range: body_range, - node: body - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }; - - let handlers: Vec = handlers - .into_iter() - .map(|node| (node, locator).into()) - .collect(); - - // Find the start and end of the `orelse`. - let orelse = (!orelse.is_empty()).then(|| { - let orelse_range = expand_indented_block( - handlers.last().map_or(body.end(), Attributed::end), - orelse.last().unwrap().end(), - locator, - ); - Body { - range: orelse_range, - node: orelse - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }); - - // Find the start and end of the `finalbody`. - let finalbody = (!finalbody.is_empty()).then(|| { - let finalbody_range = expand_indented_block( - orelse.as_ref().map_or( - handlers.last().map_or(body.end(), Attributed::end), - Attributed::end, - ), - finalbody.last().unwrap().end(), - locator, - ); - Body { - range: finalbody_range, - node: finalbody - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - trivia: vec![], - parentheses: Parenthesize::Never, - } - }); - - let end_location = finalbody.as_ref().map_or( - orelse.as_ref().map_or( - handlers.last().map_or(body.end(), Attributed::end), - Attributed::end, - ), - Attributed::end, - ); - - Stmt { - range: TextRange::new(range.start(), end_location), - node: StmtKind::TryStar { - body, - handlers, - orelse, - finalbody, - }, - trivia: vec![], - parentheses: Parenthesize::Never, - } - } - ast::Stmt::Import(ast::StmtImport { names, range }) => Stmt { - range, - node: StmtKind::Import { - names: names - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Stmt::ImportFrom(ast::StmtImportFrom { - module, - names, - level, - range, - }) => Stmt { - range, - node: StmtKind::ImportFrom { - module: module.map(Into::into), - names: names - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - level: level.map(|level| level.to_u32()), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Stmt::Global(ast::StmtGlobal { names, range }) => Stmt { - range, - node: StmtKind::Global { - names: names.into_iter().map(Into::into).collect(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Stmt::Nonlocal(ast::StmtNonlocal { names, range }) => Stmt { - range, - node: StmtKind::Nonlocal { - names: names.into_iter().map(Into::into).collect(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Stmt::Break(ast::StmtBreak { range }) => Stmt { - range, - node: StmtKind::Break, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Stmt::Continue(ast::StmtContinue { range }) => Stmt { - range, - node: StmtKind::Continue, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - } - } -} - -impl From<(ast::Keyword, &Locator<'_>)> for Keyword { - fn from((keyword, locator): (ast::Keyword, &Locator)) -> Self { - Keyword { - range: keyword.range(), - node: KeywordData { - arg: keyword.arg.map(Into::into), - value: (keyword.value, locator).into(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - } - } -} - -impl From<(ast::Arg, &Locator<'_>)> for Arg { - fn from((arg, locator): (ast::Arg, &Locator)) -> Self { - Arg { - range: arg.range(), - node: ArgData { - arg: arg.arg.into(), - annotation: arg.annotation.map(|node| Box::new((*node, locator).into())), - type_comment: arg.type_comment, - }, - trivia: vec![], - parentheses: Parenthesize::Never, - } - } -} - -impl From<(ast::Arguments, &Locator<'_>)> for Arguments { - fn from((arguments, locator): (ast::Arguments, &Locator)) -> Self { - Arguments { - posonlyargs: arguments - .posonlyargs - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - args: arguments - .args - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - vararg: arguments - .vararg - .map(|node| Box::new((*node, locator).into())), - kwonlyargs: arguments - .kwonlyargs - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - kw_defaults: arguments - .kw_defaults - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - kwarg: arguments - .kwarg - .map(|node| Box::new((*node, locator).into())), - defaults: arguments - .defaults - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - } - } -} - -impl From<(ast::Comprehension, &Locator<'_>)> for Comprehension { - fn from((comprehension, locator): (ast::Comprehension, &Locator)) -> Self { - Comprehension { - target: (comprehension.target, locator).into(), - iter: (comprehension.iter, locator).into(), - ifs: comprehension - .ifs - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - is_async: usize::from(comprehension.is_async), - } - } -} - -impl From<(ast::Expr, &Locator<'_>)> for Expr { - fn from((expr, locator): (ast::Expr, &Locator)) -> Self { - match expr { - ast::Expr::Name(ast::ExprName { id, ctx, range }) => Expr { - range, - node: ExprKind::Name { - id: id.into(), - ctx: ctx.into(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::BoolOp(ast::ExprBoolOp { op, values, range }) => Expr { - range, - node: ExprKind::BoolOp { - ops: values - .iter() - .tuple_windows() - .map(|(left, right)| { - let target_tok = match &op { - ast::Boolop::And => rustpython_parser::Tok::And, - ast::Boolop::Or => rustpython_parser::Tok::Or, - }; - let op_range = find_tok( - TextRange::new(left.end(), right.start()), - locator, - |tok| tok == target_tok, - ); - BoolOp::new(op_range, (&op).into()) - }) - .collect(), - values: values - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::NamedExpr(ast::ExprNamedExpr { - target, - value, - range, - }) => Expr { - range, - node: ExprKind::NamedExpr { - target: Box::new((*target, locator).into()), - value: Box::new((*value, locator).into()), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::BinOp(ast::ExprBinOp { - left, - op, - right, - range, - }) => Expr { - range, - node: ExprKind::BinOp { - op: { - let target_tok = match &op { - ast::Operator::Add => rustpython_parser::Tok::Plus, - ast::Operator::Sub => rustpython_parser::Tok::Minus, - ast::Operator::Mult => rustpython_parser::Tok::Star, - ast::Operator::MatMult => rustpython_parser::Tok::At, - ast::Operator::Div => rustpython_parser::Tok::Slash, - ast::Operator::Mod => rustpython_parser::Tok::Percent, - ast::Operator::Pow => rustpython_parser::Tok::DoubleStar, - ast::Operator::LShift => rustpython_parser::Tok::LeftShift, - ast::Operator::RShift => rustpython_parser::Tok::RightShift, - ast::Operator::BitOr => rustpython_parser::Tok::Vbar, - ast::Operator::BitXor => rustpython_parser::Tok::CircumFlex, - ast::Operator::BitAnd => rustpython_parser::Tok::Amper, - ast::Operator::FloorDiv => rustpython_parser::Tok::DoubleSlash, - }; - let op_range = - find_tok(TextRange::new(left.end(), right.start()), locator, |tok| { - tok == target_tok - }); - Operator::new(op_range, (&op).into()) - }, - left: Box::new((*left, locator).into()), - right: Box::new((*right, locator).into()), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::UnaryOp(ast::ExprUnaryOp { op, operand, range }) => Expr { - range, - node: ExprKind::UnaryOp { - op: { - let target_tok = match &op { - ast::Unaryop::Invert => rustpython_parser::Tok::Tilde, - ast::Unaryop::Not => rustpython_parser::Tok::Not, - ast::Unaryop::UAdd => rustpython_parser::Tok::Plus, - ast::Unaryop::USub => rustpython_parser::Tok::Minus, - }; - let op_range = find_tok( - TextRange::new(range.start(), operand.start()), - locator, - |tok| tok == target_tok, - ); - UnaryOp::new(op_range, (&op).into()) - }, - operand: Box::new((*operand, locator).into()), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::Lambda(ast::ExprLambda { args, body, range }) => Expr { - range, - node: ExprKind::Lambda { - args: Box::new((*args, locator).into()), - body: Box::new((*body, locator).into()), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::IfExp(ast::ExprIfExp { - test, - body, - orelse, - range, - }) => Expr { - range, - node: ExprKind::IfExp { - test: Box::new((*test, locator).into()), - body: Box::new((*body, locator).into()), - orelse: Box::new((*orelse, locator).into()), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::Dict(ast::ExprDict { - keys, - values, - range, - }) => Expr { - range, - node: ExprKind::Dict { - keys: keys - .into_iter() - .map(|key| key.map(|node| (node, locator).into())) - .collect(), - values: values - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::Set(ast::ExprSet { elts, range }) => Expr { - range, - node: ExprKind::Set { - elts: elts - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::ListComp(ast::ExprListComp { - elt, - generators, - range, - }) => Expr { - range, - node: ExprKind::ListComp { - elt: Box::new((*elt, locator).into()), - generators: generators - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::SetComp(ast::ExprSetComp { - elt, - generators, - range, - }) => Expr { - range, - node: ExprKind::SetComp { - elt: Box::new((*elt, locator).into()), - generators: generators - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::DictComp(ast::ExprDictComp { - key, - value, - generators, - range, - }) => Expr { - range, - node: ExprKind::DictComp { - key: Box::new((*key, locator).into()), - value: Box::new((*value, locator).into()), - generators: generators - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::GeneratorExp(ast::ExprGeneratorExp { - elt, - generators, - range, - }) => Expr { - range, - node: ExprKind::GeneratorExp { - elt: Box::new((*elt, locator).into()), - generators: generators - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::Await(ast::ExprAwait { value, range }) => Expr { - range, - node: ExprKind::Await { - value: Box::new((*value, locator).into()), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::Yield(ast::ExprYield { value, range }) => Expr { - range, - node: ExprKind::Yield { - value: value.map(|v| Box::new((*v, locator).into())), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::YieldFrom(ast::ExprYieldFrom { value, range }) => Expr { - range, - node: ExprKind::YieldFrom { - value: Box::new((*value, locator).into()), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::Compare(ast::ExprCompare { - left, - ops, - comparators, - range, - }) => Expr { - range, - node: ExprKind::Compare { - ops: iter::once(left.as_ref()) - .chain(comparators.iter()) - .tuple_windows() - .zip(ops.into_iter()) - .map(|((left, right), op)| { - let target_tok = match &op { - ast::Cmpop::Eq => rustpython_parser::Tok::EqEqual, - ast::Cmpop::NotEq => rustpython_parser::Tok::NotEqual, - ast::Cmpop::Lt => rustpython_parser::Tok::Less, - ast::Cmpop::LtE => rustpython_parser::Tok::LessEqual, - ast::Cmpop::Gt => rustpython_parser::Tok::Greater, - ast::Cmpop::GtE => rustpython_parser::Tok::GreaterEqual, - ast::Cmpop::Is => rustpython_parser::Tok::Is, - // TODO(charlie): Break this into two tokens. - ast::Cmpop::IsNot => rustpython_parser::Tok::Is, - ast::Cmpop::In => rustpython_parser::Tok::In, - // TODO(charlie): Break this into two tokens. - ast::Cmpop::NotIn => rustpython_parser::Tok::In, - }; - let op_range = find_tok( - TextRange::new(left.end(), right.start()), - locator, - |tok| tok == target_tok, - ); - CmpOp::new(op_range, (&op).into()) - }) - .collect(), - left: Box::new((*left, locator).into()), - comparators: comparators - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::Call(ast::ExprCall { - func, - args, - keywords, - range, - }) => Expr { - range, - node: ExprKind::Call { - func: Box::new((*func, locator).into()), - args: args - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - keywords: keywords - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::FormattedValue(ast::ExprFormattedValue { - value, - conversion, - format_spec, - range, - }) => Expr { - range, - node: ExprKind::FormattedValue { - value: Box::new((*value, locator).into()), - conversion, - format_spec: format_spec.map(|f| Box::new((*f, locator).into())), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::JoinedStr(ast::ExprJoinedStr { values, range }) => Expr { - range, - node: ExprKind::JoinedStr { - values: values - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::Constant(ast::ExprConstant { value, kind, range }) => Expr { - range, - node: ExprKind::Constant { value, kind }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::Attribute(ast::ExprAttribute { - value, - attr, - ctx, - range, - }) => Expr { - range, - node: ExprKind::Attribute { - value: Box::new((*value, locator).into()), - attr: attr.into(), - ctx: ctx.into(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::Subscript(ast::ExprSubscript { - value, - slice, - ctx, - range, - }) => Expr { - range, - node: ExprKind::Subscript { - value: Box::new((*value, locator).into()), - slice: Box::new((*slice, locator).into()), - ctx: ctx.into(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::Starred(ast::ExprStarred { value, ctx, range }) => Expr { - range, - node: ExprKind::Starred { - value: Box::new((*value, locator).into()), - ctx: ctx.into(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::List(ast::ExprList { elts, ctx, range }) => Expr { - range, - node: ExprKind::List { - elts: elts - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - ctx: ctx.into(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::Tuple(ast::ExprTuple { elts, ctx, range }) => Expr { - range, - node: ExprKind::Tuple { - elts: elts - .into_iter() - .map(|node| (node, locator).into()) - .collect(), - ctx: ctx.into(), - }, - trivia: vec![], - parentheses: Parenthesize::Never, - }, - ast::Expr::Slice(ast::ExprSlice { - lower, - upper, - step, - range: expr_range, - }) => { - // Locate the colon tokens, which indicate the number of index segments. - let tokens = rustpython_parser::lexer::lex_starts_at( - &locator.contents()[expr_range], - Mode::Module, - expr_range.start(), - ); - - // Find the first and (if it exists) second colon in the slice, avoiding any - // semicolons within nested slices, and any lambda expressions. - let mut first_colon = None; - let mut second_colon = None; - let mut lambda = 0; - let mut nesting = 0; - for (tok, range) in tokens.flatten() { - match tok { - rustpython_parser::Tok::Lambda if nesting == 0 => lambda += 1, - rustpython_parser::Tok::Colon if nesting == 0 => { - if lambda > 0 { - lambda -= 1; - } else { - if first_colon.is_none() { - first_colon = Some(range.start()); - } else { - second_colon = Some(range.start()); - break; - } - } - } - rustpython_parser::Tok::Lpar - | rustpython_parser::Tok::Lsqb - | rustpython_parser::Tok::Lbrace => nesting += 1, - rustpython_parser::Tok::Rpar - | rustpython_parser::Tok::Rsqb - | rustpython_parser::Tok::Rbrace => nesting -= 1, - _ => {} - } - } - - let lower = SliceIndex::new( - TextRange::new(expr_range.start(), first_colon.unwrap()), - lower.map_or(SliceIndexKind::Empty, |node| SliceIndexKind::Index { - value: Box::new((*node, locator).into()), - }), - ); - let upper = SliceIndex::new( - TextRange::new( - first_colon.unwrap(), - second_colon.unwrap_or(expr_range.end()), - ), - upper.map_or(SliceIndexKind::Empty, |node| SliceIndexKind::Index { - value: Box::new((*node, locator).into()), - }), - ); - let step = second_colon.map(|second_colon| { - SliceIndex::new( - TextRange::new(second_colon, expr_range.end()), - step.map_or(SliceIndexKind::Empty, |node| SliceIndexKind::Index { - value: Box::new((*node, locator).into()), - }), - ) - }); - - Expr { - range: expr_range, - node: ExprKind::Slice { lower, upper, step }, - trivia: vec![], - parentheses: Parenthesize::Never, - } - } - } - } -} diff --git a/crates/ruff_python_formatter/src/cst/visitor.rs b/crates/ruff_python_formatter/src/cst/visitor.rs deleted file mode 100644 index 72c64e6fb6a42..0000000000000 --- a/crates/ruff_python_formatter/src/cst/visitor.rs +++ /dev/null @@ -1,634 +0,0 @@ -use rustpython_parser::ast::Constant; - -use crate::cst::{ - Alias, Arg, Arguments, Body, BoolOp, CmpOp, Comprehension, Excepthandler, ExcepthandlerKind, - Expr, ExprContext, ExprKind, Keyword, MatchCase, Operator, Pattern, PatternKind, SliceIndex, - SliceIndexKind, Stmt, StmtKind, UnaryOp, Withitem, -}; - -pub(crate) trait Visitor<'a> { - fn visit_stmt(&mut self, stmt: &'a mut Stmt) { - walk_stmt(self, stmt); - } - fn visit_annotation(&mut self, expr: &'a mut Expr) { - walk_expr(self, expr); - } - fn visit_expr(&mut self, expr: &'a mut Expr) { - walk_expr(self, expr); - } - fn visit_constant(&mut self, constant: &'a mut Constant) { - walk_constant(self, constant); - } - fn visit_expr_context(&mut self, expr_context: &'a mut ExprContext) { - walk_expr_context(self, expr_context); - } - fn visit_bool_op(&mut self, bool_op: &'a mut BoolOp) { - walk_bool_op(self, bool_op); - } - fn visit_operator(&mut self, operator: &'a mut Operator) { - walk_operator(self, operator); - } - fn visit_unary_op(&mut self, unary_op: &'a mut UnaryOp) { - walk_unary_op(self, unary_op); - } - fn visit_cmp_op(&mut self, cmp_op: &'a mut CmpOp) { - walk_cmp_op(self, cmp_op); - } - fn visit_comprehension(&mut self, comprehension: &'a mut Comprehension) { - walk_comprehension(self, comprehension); - } - fn visit_excepthandler(&mut self, excepthandler: &'a mut Excepthandler) { - walk_excepthandler(self, excepthandler); - } - fn visit_slice_index(&mut self, slice_index: &'a mut SliceIndex) { - walk_slice_index(self, slice_index); - } - fn visit_format_spec(&mut self, format_spec: &'a mut Expr) { - walk_expr(self, format_spec); - } - fn visit_arguments(&mut self, arguments: &'a mut Arguments) { - walk_arguments(self, arguments); - } - fn visit_arg(&mut self, arg: &'a mut Arg) { - walk_arg(self, arg); - } - fn visit_keyword(&mut self, keyword: &'a mut Keyword) { - walk_keyword(self, keyword); - } - fn visit_alias(&mut self, alias: &'a mut Alias) { - walk_alias(self, alias); - } - fn visit_withitem(&mut self, withitem: &'a mut Withitem) { - walk_withitem(self, withitem); - } - fn visit_match_case(&mut self, match_case: &'a mut MatchCase) { - walk_match_case(self, match_case); - } - fn visit_pattern(&mut self, pattern: &'a mut Pattern) { - walk_pattern(self, pattern); - } - fn visit_body(&mut self, body: &'a mut Body) { - walk_body(self, body); - } -} - -pub(crate) fn walk_body<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, body: &'a mut Body) { - for stmt in &mut body.node { - visitor.visit_stmt(stmt); - } -} - -pub(crate) fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a mut Stmt) { - match &mut stmt.node { - StmtKind::FunctionDef { - args, - body, - decorator_list, - returns, - .. - } => { - visitor.visit_arguments(args); - for expr in decorator_list { - visitor.visit_expr(expr); - } - for expr in returns { - visitor.visit_annotation(expr); - } - visitor.visit_body(body); - } - StmtKind::AsyncFunctionDef { - args, - body, - decorator_list, - returns, - .. - } => { - visitor.visit_arguments(args); - for expr in decorator_list { - visitor.visit_expr(expr); - } - for expr in returns { - visitor.visit_annotation(expr); - } - visitor.visit_body(body); - } - StmtKind::ClassDef { - bases, - keywords, - body, - decorator_list, - .. - } => { - for expr in bases { - visitor.visit_expr(expr); - } - for keyword in keywords { - visitor.visit_keyword(keyword); - } - for expr in decorator_list { - visitor.visit_expr(expr); - } - visitor.visit_body(body); - } - StmtKind::Return { value } => { - if let Some(expr) = value { - visitor.visit_expr(expr); - } - } - StmtKind::Delete { targets } => { - for expr in targets { - visitor.visit_expr(expr); - } - } - StmtKind::Assign { targets, value, .. } => { - visitor.visit_expr(value); - for expr in targets { - visitor.visit_expr(expr); - } - } - StmtKind::AugAssign { target, op, value } => { - visitor.visit_expr(target); - visitor.visit_operator(op); - visitor.visit_expr(value); - } - StmtKind::AnnAssign { - target, - annotation, - value, - .. - } => { - visitor.visit_annotation(annotation); - if let Some(expr) = value { - visitor.visit_expr(expr); - } - visitor.visit_expr(target); - } - StmtKind::For { - target, - iter, - body, - orelse, - .. - } => { - visitor.visit_expr(iter); - visitor.visit_expr(target); - visitor.visit_body(body); - if let Some(orelse) = orelse { - visitor.visit_body(orelse); - } - } - StmtKind::AsyncFor { - target, - iter, - body, - orelse, - .. - } => { - visitor.visit_expr(iter); - visitor.visit_expr(target); - visitor.visit_body(body); - if let Some(orelse) = orelse { - visitor.visit_body(orelse); - } - } - StmtKind::While { test, body, orelse } => { - visitor.visit_expr(test); - visitor.visit_body(body); - if let Some(orelse) = orelse { - visitor.visit_body(orelse); - } - } - StmtKind::If { - test, body, orelse, .. - } => { - visitor.visit_expr(test); - visitor.visit_body(body); - if let Some(orelse) = orelse { - visitor.visit_body(orelse); - } - } - StmtKind::With { items, body, .. } => { - for withitem in items { - visitor.visit_withitem(withitem); - } - visitor.visit_body(body); - } - StmtKind::AsyncWith { items, body, .. } => { - for withitem in items { - visitor.visit_withitem(withitem); - } - visitor.visit_body(body); - } - StmtKind::Match { subject, cases } => { - visitor.visit_expr(subject); - for match_case in cases { - visitor.visit_match_case(match_case); - } - } - StmtKind::Raise { exc, cause } => { - if let Some(expr) = exc { - visitor.visit_expr(expr); - }; - if let Some(expr) = cause { - visitor.visit_expr(expr); - }; - } - StmtKind::Try { - body, - handlers, - orelse, - finalbody, - } => { - visitor.visit_body(body); - for excepthandler in handlers { - visitor.visit_excepthandler(excepthandler); - } - if let Some(orelse) = orelse { - visitor.visit_body(orelse); - } - if let Some(finalbody) = finalbody { - visitor.visit_body(finalbody); - } - } - StmtKind::TryStar { - body, - handlers, - orelse, - finalbody, - } => { - visitor.visit_body(body); - for excepthandler in handlers { - visitor.visit_excepthandler(excepthandler); - } - if let Some(orelse) = orelse { - visitor.visit_body(orelse); - } - if let Some(finalbody) = finalbody { - visitor.visit_body(finalbody); - } - } - StmtKind::Assert { test, msg } => { - visitor.visit_expr(test); - if let Some(expr) = msg { - visitor.visit_expr(expr); - } - } - StmtKind::Import { names } => { - for alias in names { - visitor.visit_alias(alias); - } - } - StmtKind::ImportFrom { names, .. } => { - for alias in names { - visitor.visit_alias(alias); - } - } - StmtKind::Global { .. } => {} - StmtKind::Nonlocal { .. } => {} - StmtKind::Expr { value } => visitor.visit_expr(value), - StmtKind::Pass => {} - StmtKind::Break => {} - StmtKind::Continue => {} - } -} - -pub(crate) fn walk_expr<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, expr: &'a mut Expr) { - match &mut expr.node { - ExprKind::BoolOp { ops, values } => { - for op in ops { - visitor.visit_bool_op(op); - } - for value in values { - visitor.visit_expr(value); - } - } - ExprKind::NamedExpr { target, value } => { - visitor.visit_expr(target); - visitor.visit_expr(value); - } - ExprKind::BinOp { left, op, right } => { - visitor.visit_expr(left); - visitor.visit_operator(op); - visitor.visit_expr(right); - } - ExprKind::UnaryOp { op, operand } => { - visitor.visit_unary_op(op); - visitor.visit_expr(operand); - } - ExprKind::Lambda { args, body } => { - visitor.visit_arguments(args); - visitor.visit_expr(body); - } - ExprKind::IfExp { test, body, orelse } => { - visitor.visit_expr(test); - visitor.visit_expr(body); - visitor.visit_expr(orelse); - } - ExprKind::Dict { keys, values } => { - for expr in keys.iter_mut().flatten() { - visitor.visit_expr(expr); - } - for expr in values { - visitor.visit_expr(expr); - } - } - ExprKind::Set { elts } => { - for expr in elts { - visitor.visit_expr(expr); - } - } - ExprKind::ListComp { elt, generators } => { - for comprehension in generators { - visitor.visit_comprehension(comprehension); - } - visitor.visit_expr(elt); - } - ExprKind::SetComp { elt, generators } => { - for comprehension in generators { - visitor.visit_comprehension(comprehension); - } - visitor.visit_expr(elt); - } - ExprKind::DictComp { - key, - value, - generators, - } => { - for comprehension in generators { - visitor.visit_comprehension(comprehension); - } - visitor.visit_expr(key); - visitor.visit_expr(value); - } - ExprKind::GeneratorExp { elt, generators } => { - for comprehension in generators { - visitor.visit_comprehension(comprehension); - } - visitor.visit_expr(elt); - } - ExprKind::Await { value } => visitor.visit_expr(value), - ExprKind::Yield { value } => { - if let Some(expr) = value { - visitor.visit_expr(expr); - } - } - ExprKind::YieldFrom { value } => visitor.visit_expr(value), - ExprKind::Compare { - left, - ops, - comparators, - } => { - visitor.visit_expr(left); - for cmpop in ops { - visitor.visit_cmp_op(cmpop); - } - for expr in comparators { - visitor.visit_expr(expr); - } - } - ExprKind::Call { - func, - args, - keywords, - } => { - visitor.visit_expr(func); - for expr in args { - visitor.visit_expr(expr); - } - for keyword in keywords { - visitor.visit_keyword(keyword); - } - } - ExprKind::FormattedValue { - value, format_spec, .. - } => { - visitor.visit_expr(value); - if let Some(expr) = format_spec { - visitor.visit_format_spec(expr); - } - } - ExprKind::JoinedStr { values } => { - for expr in values { - visitor.visit_expr(expr); - } - } - ExprKind::Constant { value, .. } => visitor.visit_constant(value), - ExprKind::Attribute { value, ctx, .. } => { - visitor.visit_expr(value); - visitor.visit_expr_context(ctx); - } - ExprKind::Subscript { value, slice, ctx } => { - visitor.visit_expr(value); - visitor.visit_expr(slice); - visitor.visit_expr_context(ctx); - } - ExprKind::Starred { value, ctx } => { - visitor.visit_expr(value); - visitor.visit_expr_context(ctx); - } - ExprKind::Name { ctx, .. } => { - visitor.visit_expr_context(ctx); - } - ExprKind::List { elts, ctx } => { - for expr in elts { - visitor.visit_expr(expr); - } - visitor.visit_expr_context(ctx); - } - ExprKind::Tuple { elts, ctx } => { - for expr in elts { - visitor.visit_expr(expr); - } - visitor.visit_expr_context(ctx); - } - ExprKind::Slice { lower, upper, step } => { - visitor.visit_slice_index(lower); - visitor.visit_slice_index(upper); - if let Some(expr) = step { - visitor.visit_slice_index(expr); - } - } - } -} - -pub(crate) fn walk_constant<'a, V: Visitor<'a> + ?Sized>( - visitor: &mut V, - constant: &'a mut Constant, -) { - if let Constant::Tuple(constants) = constant { - for constant in constants { - visitor.visit_constant(constant); - } - } -} - -pub(crate) fn walk_comprehension<'a, V: Visitor<'a> + ?Sized>( - visitor: &mut V, - comprehension: &'a mut Comprehension, -) { - visitor.visit_expr(&mut comprehension.iter); - visitor.visit_expr(&mut comprehension.target); - for expr in &mut comprehension.ifs { - visitor.visit_expr(expr); - } -} - -pub(crate) fn walk_excepthandler<'a, V: Visitor<'a> + ?Sized>( - visitor: &mut V, - excepthandler: &'a mut Excepthandler, -) { - match &mut excepthandler.node { - ExcepthandlerKind::ExceptHandler { type_, body, .. } => { - if let Some(expr) = type_ { - visitor.visit_expr(expr); - } - visitor.visit_body(body); - } - } -} - -pub(crate) fn walk_slice_index<'a, V: Visitor<'a> + ?Sized>( - visitor: &mut V, - slice_index: &'a mut SliceIndex, -) { - match &mut slice_index.node { - SliceIndexKind::Empty => {} - SliceIndexKind::Index { value } => { - visitor.visit_expr(value); - } - } -} - -pub(crate) fn walk_arguments<'a, V: Visitor<'a> + ?Sized>( - visitor: &mut V, - arguments: &'a mut Arguments, -) { - for arg in &mut arguments.posonlyargs { - visitor.visit_arg(arg); - } - for arg in &mut arguments.args { - visitor.visit_arg(arg); - } - if let Some(arg) = &mut arguments.vararg { - visitor.visit_arg(arg); - } - for arg in &mut arguments.kwonlyargs { - visitor.visit_arg(arg); - } - for expr in &mut arguments.kw_defaults { - visitor.visit_expr(expr); - } - if let Some(arg) = &mut arguments.kwarg { - visitor.visit_arg(arg); - } - for expr in &mut arguments.defaults { - visitor.visit_expr(expr); - } -} - -pub(crate) fn walk_arg<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, arg: &'a mut Arg) { - if let Some(expr) = &mut arg.node.annotation { - visitor.visit_annotation(expr); - } -} - -pub(crate) fn walk_keyword<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, keyword: &'a mut Keyword) { - visitor.visit_expr(&mut keyword.node.value); -} - -pub(crate) fn walk_withitem<'a, V: Visitor<'a> + ?Sized>( - visitor: &mut V, - withitem: &'a mut Withitem, -) { - visitor.visit_expr(&mut withitem.context_expr); - if let Some(expr) = &mut withitem.optional_vars { - visitor.visit_expr(expr); - } -} - -pub(crate) fn walk_match_case<'a, V: Visitor<'a> + ?Sized>( - visitor: &mut V, - match_case: &'a mut MatchCase, -) { - visitor.visit_pattern(&mut match_case.pattern); - if let Some(expr) = &mut match_case.guard { - visitor.visit_expr(expr); - } - visitor.visit_body(&mut match_case.body); -} - -pub(crate) fn walk_pattern<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, pattern: &'a mut Pattern) { - match &mut pattern.node { - PatternKind::MatchValue { value } => visitor.visit_expr(value), - PatternKind::MatchSingleton { value } => visitor.visit_constant(value), - PatternKind::MatchSequence { patterns } => { - for pattern in patterns { - visitor.visit_pattern(pattern); - } - } - PatternKind::MatchMapping { keys, patterns, .. } => { - for expr in keys { - visitor.visit_expr(expr); - } - for pattern in patterns { - visitor.visit_pattern(pattern); - } - } - PatternKind::MatchClass { - cls, - patterns, - kwd_patterns, - .. - } => { - visitor.visit_expr(cls); - for pattern in patterns { - visitor.visit_pattern(pattern); - } - - for pattern in kwd_patterns { - visitor.visit_pattern(pattern); - } - } - PatternKind::MatchStar { .. } => {} - PatternKind::MatchAs { pattern, .. } => { - if let Some(pattern) = pattern { - visitor.visit_pattern(pattern); - } - } - PatternKind::MatchOr { patterns } => { - for pattern in patterns { - visitor.visit_pattern(pattern); - } - } - } -} - -#[allow(unused_variables)] -pub(crate) fn walk_expr_context<'a, V: Visitor<'a> + ?Sized>( - visitor: &mut V, - expr_context: &'a mut ExprContext, -) { -} - -#[allow(unused_variables)] -pub(crate) fn walk_bool_op<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, bool_op: &'a mut BoolOp) {} - -#[allow(unused_variables)] -pub(crate) fn walk_operator<'a, V: Visitor<'a> + ?Sized>( - visitor: &mut V, - operator: &'a mut Operator, -) { -} - -#[allow(unused_variables)] -pub(crate) fn walk_unary_op<'a, V: Visitor<'a> + ?Sized>( - visitor: &mut V, - unary_op: &'a mut UnaryOp, -) { -} - -#[allow(unused_variables)] -pub(crate) fn walk_cmp_op<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, cmp_op: &'a mut CmpOp) {} - -#[allow(unused_variables)] -pub(crate) fn walk_alias<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, alias: &'a mut Alias) {} diff --git a/crates/ruff_python_formatter/src/format/alias.rs b/crates/ruff_python_formatter/src/format/alias.rs deleted file mode 100644 index a6d3f1e189117..0000000000000 --- a/crates/ruff_python_formatter/src/format/alias.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::prelude::*; -use ruff_formatter::write; - -use crate::cst::Alias; -use crate::format::comments::end_of_line_comments; - -pub(crate) struct FormatAlias<'a> { - item: &'a Alias, -} - -impl AsFormat> for Alias { - type Format<'a> = FormatAlias<'a>; - - fn format(&self) -> Self::Format<'_> { - FormatAlias { item: self } - } -} - -impl Format> for FormatAlias<'_> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let alias = self.item; - - write!(f, [dynamic_text(&alias.name, None)])?; - if let Some(asname) = &alias.asname { - write!(f, [text(" as ")])?; - write!(f, [dynamic_text(asname, None)])?; - } - - write!(f, [end_of_line_comments(alias)])?; - - Ok(()) - } -} diff --git a/crates/ruff_python_formatter/src/format/arg.rs b/crates/ruff_python_formatter/src/format/arg.rs deleted file mode 100644 index f350a464dfa2e..0000000000000 --- a/crates/ruff_python_formatter/src/format/arg.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::prelude::*; -use ruff_formatter::write; - -use crate::cst::Arg; -use crate::format::comments::end_of_line_comments; - -pub(crate) struct FormatArg<'a> { - item: &'a Arg, -} - -impl AsFormat> for Arg { - type Format<'a> = FormatArg<'a>; - - fn format(&self) -> Self::Format<'_> { - FormatArg { item: self } - } -} - -impl Format> for FormatArg<'_> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let arg = self.item; - - write!(f, [dynamic_text(&arg.arg, None)])?; - if let Some(annotation) = &arg.annotation { - write!(f, [text(": ")])?; - write!(f, [annotation.format()])?; - } - write!(f, [end_of_line_comments(arg)])?; - - Ok(()) - } -} diff --git a/crates/ruff_python_formatter/src/format/arguments.rs b/crates/ruff_python_formatter/src/format/arguments.rs deleted file mode 100644 index fe8dbc5e5a636..0000000000000 --- a/crates/ruff_python_formatter/src/format/arguments.rs +++ /dev/null @@ -1,121 +0,0 @@ -use crate::prelude::*; -use ruff_formatter::{format_args, write, Format}; - -use crate::cst::Arguments; - -pub(crate) struct FormatArguments<'a> { - item: &'a Arguments, -} - -impl AsFormat> for Arguments { - type Format<'a> = FormatArguments<'a>; - - fn format(&self) -> Self::Format<'_> { - FormatArguments { item: self } - } -} - -impl Format> for FormatArguments<'_> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let args = self.item; - - let mut first = true; - - let defaults_start = args.posonlyargs.len() + args.args.len() - args.defaults.len(); - for (i, arg) in args.posonlyargs.iter().chain(&args.args).enumerate() { - if !std::mem::take(&mut first) { - write!(f, [text(",")])?; - write!(f, [soft_line_break_or_space()])?; - } - - write!( - f, - [group(&format_args![format_with(|f| { - write!(f, [arg.format()])?; - if let Some(i) = i.checked_sub(defaults_start) { - if arg.annotation.is_some() { - write!(f, [space()])?; - write!(f, [text("=")])?; - write!(f, [space()])?; - } else { - write!(f, [text("=")])?; - } - write!(f, [args.defaults[i].format()])?; - } - Ok(()) - })])] - )?; - - if i + 1 == args.posonlyargs.len() { - if !std::mem::take(&mut first) { - write!(f, [text(",")])?; - write!(f, [soft_line_break_or_space()])?; - } - write!(f, [text("/")])?; - } - } - - if let Some(vararg) = &args.vararg { - if !std::mem::take(&mut first) { - write!(f, [text(",")])?; - write!(f, [soft_line_break_or_space()])?; - } - first = false; - - write!(f, [text("*")])?; - write!(f, [vararg.format()])?; - } else if !args.kwonlyargs.is_empty() { - if !std::mem::take(&mut first) { - write!(f, [text(",")])?; - write!(f, [soft_line_break_or_space()])?; - } - first = false; - - write!(f, [text("*")])?; - } - - let defaults_start = args.kwonlyargs.len() - args.kw_defaults.len(); - for (i, kwarg) in args.kwonlyargs.iter().enumerate() { - if !std::mem::take(&mut first) { - write!(f, [text(",")])?; - write!(f, [soft_line_break_or_space()])?; - } - - write!( - f, - [group(&format_args![format_with(|f| { - write!(f, [kwarg.format()])?; - if let Some(default) = i - .checked_sub(defaults_start) - .and_then(|i| args.kw_defaults.get(i)) - { - if kwarg.annotation.is_some() { - write!(f, [space()])?; - write!(f, [text("=")])?; - write!(f, [space()])?; - } else { - write!(f, [text("=")])?; - } - write!(f, [default.format()])?; - } - Ok(()) - })])] - )?; - } - if let Some(kwarg) = &args.kwarg { - if !std::mem::take(&mut first) { - write!(f, [text(",")])?; - write!(f, [soft_line_break_or_space()])?; - } - - write!(f, [text("**")])?; - write!(f, [kwarg.format()])?; - } - - if !first { - write!(f, [if_group_breaks(&text(","))])?; - } - - Ok(()) - } -} diff --git a/crates/ruff_python_formatter/src/format/bool_op.rs b/crates/ruff_python_formatter/src/format/bool_op.rs deleted file mode 100644 index 4e278ce2c0a2a..0000000000000 --- a/crates/ruff_python_formatter/src/format/bool_op.rs +++ /dev/null @@ -1,34 +0,0 @@ -use crate::prelude::*; -use ruff_formatter::write; - -use crate::cst::{BoolOp, BoolOpKind}; -use crate::format::comments::{end_of_line_comments, leading_comments, trailing_comments}; - -pub(crate) struct FormatBoolOp<'a> { - item: &'a BoolOp, -} - -impl AsFormat> for BoolOp { - type Format<'a> = FormatBoolOp<'a>; - - fn format(&self) -> Self::Format<'_> { - FormatBoolOp { item: self } - } -} - -impl Format> for FormatBoolOp<'_> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let bool_op = self.item; - write!(f, [leading_comments(bool_op)])?; - write!( - f, - [text(match bool_op.node { - BoolOpKind::And => "and", - BoolOpKind::Or => "or", - })] - )?; - write!(f, [end_of_line_comments(bool_op)])?; - write!(f, [trailing_comments(bool_op)])?; - Ok(()) - } -} diff --git a/crates/ruff_python_formatter/src/format/builders.rs b/crates/ruff_python_formatter/src/format/builders.rs deleted file mode 100644 index d22cf7bd278cf..0000000000000 --- a/crates/ruff_python_formatter/src/format/builders.rs +++ /dev/null @@ -1,101 +0,0 @@ -use crate::prelude::*; -use ruff_formatter::{write, Format}; -use ruff_text_size::TextRange; - -use crate::cst::{Body, Stmt}; -use crate::trivia::{Relationship, TriviaKind}; - -#[derive(Copy, Clone)] -pub(crate) struct Block<'a> { - body: &'a Body, -} - -impl Format> for Block<'_> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - for (i, stmt) in self.body.iter().enumerate() { - if i > 0 { - write!(f, [hard_line_break()])?; - } - write!(f, [stmt.format()])?; - } - - for trivia in &self.body.trivia { - if matches!(trivia.relationship, Relationship::Dangling) { - match trivia.kind { - TriviaKind::EmptyLine => { - write!(f, [empty_line()])?; - } - TriviaKind::OwnLineComment(range) => { - write!(f, [literal(range, ContainsNewlines::No), hard_line_break()])?; - } - _ => {} - } - } - } - - Ok(()) - } -} - -#[inline] -pub(crate) fn block(body: &Body) -> Block { - Block { body } -} - -#[derive(Copy, Clone)] -pub(crate) struct Statements<'a> { - suite: &'a [Stmt], -} - -impl Format> for Statements<'_> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - for (i, stmt) in self.suite.iter().enumerate() { - if i > 0 { - write!(f, [hard_line_break()])?; - } - write!(f, [stmt.format()])?; - } - Ok(()) - } -} - -pub(crate) fn statements(suite: &[Stmt]) -> Statements { - Statements { suite } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub(crate) struct Literal { - range: TextRange, -} - -impl Format> for Literal { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - source_text_slice(self.range, ContainsNewlines::Detect).fmt(f) - } -} - -#[inline] -pub(crate) const fn literal( - range: TextRange, - newlines: ContainsNewlines, -) -> SourceTextSliceBuilder { - source_text_slice(range, newlines) -} - -pub(crate) const fn join_names(names: &[String]) -> JoinNames { - JoinNames { names } -} - -pub(crate) struct JoinNames<'a> { - names: &'a [String], -} - -impl Format for JoinNames<'_> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let mut join = f.join_with(text(", ")); - for name in self.names { - join.entry(&dynamic_text(name, None)); - } - join.finish() - } -} diff --git a/crates/ruff_python_formatter/src/format/cmp_op.rs b/crates/ruff_python_formatter/src/format/cmp_op.rs deleted file mode 100644 index 5b32e0a4be1fc..0000000000000 --- a/crates/ruff_python_formatter/src/format/cmp_op.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::prelude::*; -use ruff_formatter::write; - -use crate::cst::{CmpOp, CmpOpKind}; -use crate::format::comments::{end_of_line_comments, leading_comments, trailing_comments}; - -pub(crate) struct FormatCmpOp<'a> { - item: &'a CmpOp, -} - -impl AsFormat> for CmpOp { - type Format<'a> = FormatCmpOp<'a>; - - fn format(&self) -> Self::Format<'_> { - FormatCmpOp { item: self } - } -} - -impl Format> for FormatCmpOp<'_> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let cmp_op = self.item; - write!(f, [leading_comments(cmp_op)])?; - write!( - f, - [text(match cmp_op.node { - CmpOpKind::Eq => "==", - CmpOpKind::NotEq => "!=", - CmpOpKind::Lt => "<", - CmpOpKind::LtE => "<=", - CmpOpKind::Gt => ">", - CmpOpKind::GtE => ">=", - CmpOpKind::Is => "is", - CmpOpKind::IsNot => "is not", - CmpOpKind::In => "in", - CmpOpKind::NotIn => "not in", - })] - )?; - write!(f, [end_of_line_comments(cmp_op)])?; - write!(f, [trailing_comments(cmp_op)])?; - Ok(()) - } -} diff --git a/crates/ruff_python_formatter/src/format/comments.rs b/crates/ruff_python_formatter/src/format/comments.rs deleted file mode 100644 index b6d13f021d443..0000000000000 --- a/crates/ruff_python_formatter/src/format/comments.rs +++ /dev/null @@ -1,117 +0,0 @@ -use crate::prelude::*; -use ruff_formatter::{write, Format}; - -use crate::cst::Attributed; -use crate::format::builders::literal; -use crate::trivia::TriviaKind; - -#[derive(Debug)] -pub(crate) struct LeadingComments<'a, T> { - item: &'a Attributed, -} - -impl Format> for LeadingComments<'_, T> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - for trivia in &self.item.trivia { - if trivia.relationship.is_leading() { - match trivia.kind { - TriviaKind::EmptyLine => { - write!(f, [empty_line()])?; - } - TriviaKind::OwnLineComment(range) => { - write!(f, [literal(range, ContainsNewlines::No), hard_line_break()])?; - } - _ => {} - } - } - } - Ok(()) - } -} - -#[inline] -pub(crate) const fn leading_comments(item: &Attributed) -> LeadingComments<'_, T> { - LeadingComments { item } -} - -#[derive(Debug)] -pub(crate) struct TrailingComments<'a, T> { - item: &'a Attributed, -} - -impl Format> for TrailingComments<'_, T> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - for trivia in &self.item.trivia { - if trivia.relationship.is_trailing() { - match trivia.kind { - TriviaKind::EmptyLine => { - write!(f, [empty_line()])?; - } - TriviaKind::OwnLineComment(range) => { - write!(f, [literal(range, ContainsNewlines::No), hard_line_break()])?; - } - _ => {} - } - } - } - Ok(()) - } -} - -#[inline] -pub(crate) const fn trailing_comments(item: &Attributed) -> TrailingComments<'_, T> { - TrailingComments { item } -} - -#[derive(Debug)] -pub(crate) struct EndOfLineComments<'a, T> { - item: &'a Attributed, -} - -impl Format> for EndOfLineComments<'_, T> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let mut first = true; - for range in self - .item - .trivia - .iter() - .filter_map(|trivia| trivia.kind.end_of_line_comment()) - { - if std::mem::take(&mut first) { - write!(f, [line_suffix(&text(" "))])?; - } - write!(f, [line_suffix(&literal(range, ContainsNewlines::No))])?; - } - Ok(()) - } -} - -#[inline] -pub(crate) const fn end_of_line_comments(item: &Attributed) -> EndOfLineComments<'_, T> { - EndOfLineComments { item } -} - -#[derive(Debug)] -pub(crate) struct DanglingComments<'a, T> { - item: &'a Attributed, -} - -impl Format> for DanglingComments<'_, T> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - for trivia in &self.item.trivia { - if trivia.relationship.is_dangling() { - if let TriviaKind::OwnLineComment(range) = trivia.kind { - write!(f, [hard_line_break()])?; - write!(f, [literal(range, ContainsNewlines::No)])?; - write!(f, [hard_line_break()])?; - } - } - } - Ok(()) - } -} - -#[inline] -pub(crate) const fn dangling_comments(item: &Attributed) -> DanglingComments<'_, T> { - DanglingComments { item } -} diff --git a/crates/ruff_python_formatter/src/format/comprehension.rs b/crates/ruff_python_formatter/src/format/comprehension.rs deleted file mode 100644 index ccf981b99400b..0000000000000 --- a/crates/ruff_python_formatter/src/format/comprehension.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::prelude::*; -use ruff_formatter::write; - -use crate::cst::Comprehension; - -pub(crate) struct FormatComprehension<'a> { - item: &'a Comprehension, -} - -impl AsFormat> for Comprehension { - type Format<'a> = FormatComprehension<'a>; - - fn format(&self) -> Self::Format<'_> { - FormatComprehension { item: self } - } -} - -impl Format> for FormatComprehension<'_> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let comprehension = self.item; - - write!(f, [soft_line_break_or_space()])?; - if comprehension.is_async > 0 { - write!(f, [text("async")])?; - write!(f, [space()])?; - } - write!(f, [text("for")])?; - write!(f, [space()])?; - // TODO(charlie): If this is an unparenthesized tuple, we need to avoid expanding it. - // Should this be set on the context? - write!(f, [group(&comprehension.target.format())])?; - write!(f, [space()])?; - write!(f, [text("in")])?; - write!(f, [space()])?; - write!(f, [group(&comprehension.iter.format())])?; - for if_clause in &comprehension.ifs { - write!(f, [soft_line_break_or_space()])?; - write!(f, [text("if")])?; - write!(f, [space()])?; - write!(f, [if_clause.format()])?; - } - - Ok(()) - } -} diff --git a/crates/ruff_python_formatter/src/format/excepthandler.rs b/crates/ruff_python_formatter/src/format/excepthandler.rs deleted file mode 100644 index 2662540249899..0000000000000 --- a/crates/ruff_python_formatter/src/format/excepthandler.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::prelude::*; -use ruff_formatter::write; - -use crate::cst::{Excepthandler, ExcepthandlerKind}; -use crate::format::builders::block; -use crate::format::comments::end_of_line_comments; - -pub(crate) struct FormatExcepthandler<'a> { - item: &'a Excepthandler, -} - -impl AsFormat> for Excepthandler { - type Format<'a> = FormatExcepthandler<'a>; - - fn format(&self) -> Self::Format<'_> { - FormatExcepthandler { item: self } - } -} - -impl Format> for FormatExcepthandler<'_> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let excepthandler = self.item; - let ExcepthandlerKind::ExceptHandler { type_, name, body } = &excepthandler.node; - - write!(f, [text("except")])?; - if let Some(type_) = &type_ { - write!(f, [space(), type_.format()])?; - if let Some(name) = &name { - write!(f, [space(), text("as"), space(), dynamic_text(name, None)])?; - } - } - write!(f, [text(":")])?; - write!(f, [end_of_line_comments(excepthandler)])?; - - write!(f, [block_indent(&block(body))])?; - - Ok(()) - } -} diff --git a/crates/ruff_python_formatter/src/format/expr.rs b/crates/ruff_python_formatter/src/format/expr.rs deleted file mode 100644 index 5a7ae3282412d..0000000000000 --- a/crates/ruff_python_formatter/src/format/expr.rs +++ /dev/null @@ -1,892 +0,0 @@ -#![allow(unused_variables, clippy::too_many_arguments)] - -use rustpython_parser::ast::{Constant, ConversionFlag}; - -use crate::prelude::*; -use ruff_formatter::{format_args, write}; - -use crate::cst::{ - Arguments, BoolOp, CmpOp, Comprehension, Expr, ExprKind, Keyword, Operator, OperatorKind, - SliceIndex, SliceIndexKind, UnaryOp, UnaryOpKind, -}; -use crate::format::builders::literal; -use crate::format::comments::{dangling_comments, end_of_line_comments, leading_comments}; -use crate::format::helpers::{is_self_closing, is_simple_power, is_simple_slice}; -use crate::format::numbers::{complex_literal, float_literal, int_literal}; -use crate::format::strings::string_literal; -use crate::trivia::{Parenthesize, TriviaKind}; - -pub(crate) struct FormatExpr<'a> { - item: &'a Expr, -} - -fn format_starred( - f: &mut Formatter, - expr: &Expr, - value: &Expr, -) -> FormatResult<()> { - write!(f, [text("*"), value.format()])?; - write!(f, [end_of_line_comments(expr)])?; - Ok(()) -} - -fn format_name(f: &mut Formatter, expr: &Expr, _id: &str) -> FormatResult<()> { - write!(f, [literal(expr.range(), ContainsNewlines::No)])?; - write!(f, [end_of_line_comments(expr)])?; - Ok(()) -} - -fn format_subscript( - f: &mut Formatter, - expr: &Expr, - value: &Expr, - slice: &Expr, -) -> FormatResult<()> { - write!(f, [value.format()])?; - write!(f, [text("[")])?; - write!( - f, - [group(&format_args![soft_block_indent(&format_with(|f| { - write!(f, [slice.format()])?; - - // Apply any dangling comments. - for trivia in &expr.trivia { - if trivia.relationship.is_dangling() { - if let TriviaKind::OwnLineComment(range) = trivia.kind { - write!(f, [expand_parent()])?; - write!(f, [hard_line_break()])?; - write!(f, [literal(range, ContainsNewlines::No)])?; - } - } - } - - Ok(()) - }))])] - )?; - write!(f, [text("]")])?; - write!(f, [end_of_line_comments(expr)])?; - Ok(()) -} - -fn format_tuple( - f: &mut Formatter, - expr: &Expr, - elts: &[Expr], -) -> FormatResult<()> { - // If we're already parenthesized, avoid adding any "mandatory" parentheses. - // TODO(charlie): We also need to parenthesize tuples on the right-hand side of an - // assignment if the target is exploded. And sometimes the tuple gets exploded, like - // if the LHS is an exploded list? Lots of edge cases here. - if elts.len() == 1 { - write!( - f, - [group(&format_args![soft_block_indent(&format_with(|f| { - write!(f, [elts[0].format()])?; - write!(f, [text(",")])?; - Ok(()) - }))])] - )?; - } else if !elts.is_empty() { - write!( - f, - [group(&format_with( - |f: &mut Formatter| { - if expr.parentheses.is_if_expanded() { - write!(f, [if_group_breaks(&text("("))])?; - } - if matches!( - expr.parentheses, - Parenthesize::IfExpanded | Parenthesize::Always - ) { - write!( - f, - [soft_block_indent(&format_with( - |f: &mut Formatter| { - let magic_trailing_comma = expr - .trivia - .iter() - .any(|c| c.kind.is_magic_trailing_comma()); - let is_unbroken = - !f.context().locator().contains_line_break(expr.range()); - if magic_trailing_comma { - write!(f, [expand_parent()])?; - } - for (i, elt) in elts.iter().enumerate() { - write!(f, [elt.format()])?; - if i < elts.len() - 1 { - write!(f, [text(",")])?; - write!(f, [soft_line_break_or_space()])?; - } else { - if magic_trailing_comma || is_unbroken { - write!(f, [if_group_breaks(&text(","))])?; - } - } - } - Ok(()) - } - ))] - )?; - } else { - let magic_trailing_comma = - expr.trivia.iter().any(|c| c.kind.is_magic_trailing_comma()); - let is_unbroken = !f.context().locator().contains_line_break(expr.range()); - if magic_trailing_comma { - write!(f, [expand_parent()])?; - } - for (i, elt) in elts.iter().enumerate() { - write!(f, [elt.format()])?; - if i < elts.len() - 1 { - write!(f, [text(",")])?; - write!(f, [soft_line_break_or_space()])?; - } else { - if magic_trailing_comma || is_unbroken { - write!(f, [if_group_breaks(&text(","))])?; - } - } - } - } - if expr.parentheses.is_if_expanded() { - write!(f, [if_group_breaks(&text(")"))])?; - } - Ok(()) - } - ))] - )?; - } - Ok(()) -} - -fn format_slice( - f: &mut Formatter, - expr: &Expr, - lower: &SliceIndex, - upper: &SliceIndex, - step: Option<&SliceIndex>, -) -> FormatResult<()> { - // https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#slices - let lower_is_simple = if let SliceIndexKind::Index { value } = &lower.node { - is_simple_slice(value) - } else { - true - }; - let upper_is_simple = if let SliceIndexKind::Index { value } = &upper.node { - is_simple_slice(value) - } else { - true - }; - let step_is_simple = step.map_or(true, |step| { - if let SliceIndexKind::Index { value } = &step.node { - is_simple_slice(value) - } else { - true - } - }); - let is_simple = lower_is_simple && upper_is_simple && step_is_simple; - - write!( - f, - [group(&format_with(|f| { - if let SliceIndexKind::Index { value } = &lower.node { - write!(f, [value.format()])?; - } - - write!(f, [dangling_comments(lower)])?; - - if matches!(lower.node, SliceIndexKind::Index { .. }) { - if !is_simple { - write!(f, [space()])?; - } - } - write!(f, [text(":")])?; - write!(f, [end_of_line_comments(lower)])?; - - if let SliceIndexKind::Index { value } = &upper.node { - if !is_simple { - write!(f, [space()])?; - } - write!(f, [if_group_breaks(&soft_line_break())])?; - write!(f, [value.format()])?; - } - - write!(f, [dangling_comments(upper)])?; - write!(f, [end_of_line_comments(upper)])?; - - if let Some(step) = step { - if matches!(upper.node, SliceIndexKind::Index { .. }) { - if !is_simple { - write!(f, [space()])?; - } - } - write!(f, [text(":")])?; - - if let SliceIndexKind::Index { value } = &step.node { - if !is_simple { - write!(f, [space()])?; - } - write!(f, [if_group_breaks(&soft_line_break())])?; - write!(f, [value.format()])?; - } - - write!(f, [dangling_comments(step)])?; - write!(f, [end_of_line_comments(step)])?; - } - Ok(()) - }))] - )?; - - write!(f, [end_of_line_comments(expr)])?; - - Ok(()) -} - -fn format_formatted_value( - f: &mut Formatter, - expr: &Expr, - value: &Expr, - _conversion: ConversionFlag, - format_spec: Option<&Expr>, -) -> FormatResult<()> { - write!(f, [text("!")])?; - write!(f, [value.format()])?; - if let Some(format_spec) = format_spec { - write!(f, [text(":")])?; - write!(f, [format_spec.format()])?; - } - Ok(()) -} - -fn format_list( - f: &mut Formatter, - expr: &Expr, - elts: &[Expr], -) -> FormatResult<()> { - write!(f, [text("[")])?; - if !elts.is_empty() { - let magic_trailing_comma = expr.trivia.iter().any(|c| c.kind.is_magic_trailing_comma()); - write!( - f, - [group(&format_args![soft_block_indent(&format_with(|f| { - if magic_trailing_comma { - write!(f, [expand_parent()])?; - } - for (i, elt) in elts.iter().enumerate() { - write!(f, [elt.format()])?; - if i < elts.len() - 1 { - write!(f, [text(",")])?; - write!(f, [soft_line_break_or_space()])?; - } else { - write!(f, [if_group_breaks(&text(","))])?; - } - } - Ok(()) - }))])] - )?; - } - write!(f, [text("]")])?; - write!(f, [end_of_line_comments(expr)])?; - Ok(()) -} - -fn format_set(f: &mut Formatter, expr: &Expr, elts: &[Expr]) -> FormatResult<()> { - if elts.is_empty() { - write!(f, [text("set()")])?; - Ok(()) - } else { - write!(f, [text("{")])?; - if !elts.is_empty() { - let magic_trailing_comma = expr.trivia.iter().any(|c| c.kind.is_magic_trailing_comma()); - write!( - f, - [group(&format_args![soft_block_indent(&format_with(|f| { - if magic_trailing_comma { - write!(f, [expand_parent()])?; - } - for (i, elt) in elts.iter().enumerate() { - write!(f, [group(&format_args![elt.format()])])?; - if i < elts.len() - 1 { - write!(f, [text(",")])?; - write!(f, [soft_line_break_or_space()])?; - } else { - if magic_trailing_comma { - write!(f, [if_group_breaks(&text(","))])?; - } - } - } - Ok(()) - }))])] - )?; - } - write!(f, [text("}")])?; - Ok(()) - } -} - -fn format_call( - f: &mut Formatter, - expr: &Expr, - func: &Expr, - args: &[Expr], - keywords: &[Keyword], -) -> FormatResult<()> { - write!(f, [func.format()])?; - if args.is_empty() && keywords.is_empty() { - write!(f, [text("(")])?; - write!(f, [text(")")])?; - write!(f, [end_of_line_comments(expr)])?; - } else { - write!(f, [text("(")])?; - write!(f, [end_of_line_comments(expr)])?; - - let magic_trailing_comma = expr.trivia.iter().any(|c| c.kind.is_magic_trailing_comma()); - write!( - f, - [group(&format_args![soft_block_indent(&format_with(|f| { - if magic_trailing_comma { - write!(f, [expand_parent()])?; - } - - for (i, arg) in args.iter().enumerate() { - write!(f, [group(&format_args![arg.format()])])?; - if i < args.len() - 1 || !keywords.is_empty() { - write!(f, [text(",")])?; - write!(f, [soft_line_break_or_space()])?; - } else { - if magic_trailing_comma || (args.len() + keywords.len() > 1) { - write!(f, [if_group_breaks(&text(","))])?; - } - } - } - for (i, keyword) in keywords.iter().enumerate() { - write!( - f, - [group(&format_args![&format_with(|f| { - write!(f, [keyword.format()])?; - Ok(()) - })])] - )?; - if i < keywords.len() - 1 { - write!(f, [text(",")])?; - write!(f, [soft_line_break_or_space()])?; - } else { - if magic_trailing_comma || (args.len() + keywords.len() > 1) { - write!(f, [if_group_breaks(&text(","))])?; - } - } - } - - write!(f, [dangling_comments(expr)])?; - - Ok(()) - }))])] - )?; - write!(f, [text(")")])?; - } - Ok(()) -} - -fn format_list_comp( - f: &mut Formatter, - expr: &Expr, - elt: &Expr, - generators: &[Comprehension], -) -> FormatResult<()> { - write!(f, [text("[")])?; - write!( - f, - [group(&format_args![soft_block_indent(&format_with(|f| { - write!(f, [elt.format()])?; - for generator in generators { - write!(f, [generator.format()])?; - } - Ok(()) - }))])] - )?; - write!(f, [text("]")])?; - Ok(()) -} - -fn format_set_comp( - f: &mut Formatter, - expr: &Expr, - elt: &Expr, - generators: &[Comprehension], -) -> FormatResult<()> { - write!(f, [text("{")])?; - write!( - f, - [group(&format_args![soft_block_indent(&format_with(|f| { - write!(f, [elt.format()])?; - for generator in generators { - write!(f, [generator.format()])?; - } - Ok(()) - }))])] - )?; - write!(f, [text("}")])?; - Ok(()) -} - -fn format_dict_comp( - f: &mut Formatter, - expr: &Expr, - key: &Expr, - value: &Expr, - generators: &[Comprehension], -) -> FormatResult<()> { - write!(f, [text("{")])?; - write!( - f, - [group(&format_args![soft_block_indent(&format_with(|f| { - write!(f, [key.format()])?; - write!(f, [text(":")])?; - write!(f, [space()])?; - write!(f, [value.format()])?; - for generator in generators { - write!(f, [generator.format()])?; - } - Ok(()) - }))])] - )?; - write!(f, [text("}")])?; - Ok(()) -} - -fn format_generator_exp( - f: &mut Formatter, - expr: &Expr, - elt: &Expr, - generators: &[Comprehension], -) -> FormatResult<()> { - write!( - f, - [group(&format_args![soft_block_indent(&format_with(|f| { - write!(f, [elt.format()])?; - for generator in generators { - write!(f, [generator.format()])?; - } - Ok(()) - }))])] - )?; - Ok(()) -} - -fn format_await( - f: &mut Formatter, - expr: &Expr, - value: &Expr, -) -> FormatResult<()> { - write!(f, [text("await")])?; - write!(f, [space()])?; - if is_self_closing(value) { - write!(f, [group(&format_args![value.format()])])?; - } else { - write!( - f, - [group(&format_args![ - if_group_breaks(&text("(")), - soft_block_indent(&format_args![value.format()]), - if_group_breaks(&text(")")), - ])] - )?; - } - Ok(()) -} - -fn format_yield( - f: &mut Formatter, - expr: &Expr, - value: Option<&Expr>, -) -> FormatResult<()> { - write!(f, [text("yield")])?; - if let Some(value) = value { - write!(f, [space()])?; - if is_self_closing(value) { - write!(f, [group(&format_args![value.format()])])?; - } else { - write!( - f, - [group(&format_args![ - if_group_breaks(&text("(")), - soft_block_indent(&format_args![value.format()]), - if_group_breaks(&text(")")), - ])] - )?; - } - } - Ok(()) -} - -fn format_yield_from( - f: &mut Formatter, - expr: &Expr, - value: &Expr, -) -> FormatResult<()> { - write!( - f, - [group(&format_args![soft_block_indent(&format_with(|f| { - write!(f, [text("yield")])?; - write!(f, [space()])?; - write!(f, [text("from")])?; - write!(f, [space()])?; - if is_self_closing(value) { - write!(f, [value.format()])?; - } else { - write!( - f, - [group(&format_args![ - if_group_breaks(&text("(")), - soft_block_indent(&format_args![value.format()]), - if_group_breaks(&text(")")), - ])] - )?; - } - Ok(()) - })),])] - )?; - Ok(()) -} - -fn format_compare( - f: &mut Formatter, - expr: &Expr, - left: &Expr, - ops: &[CmpOp], - comparators: &[Expr], -) -> FormatResult<()> { - write!(f, [group(&format_args![left.format()])])?; - for (i, op) in ops.iter().enumerate() { - write!(f, [soft_line_break_or_space()])?; - write!(f, [op.format()])?; - write!(f, [space()])?; - write!(f, [group(&format_args![comparators[i].format()])])?; - } - - write!(f, [end_of_line_comments(expr)])?; - - Ok(()) -} - -fn format_joined_str( - f: &mut Formatter, - expr: &Expr, - _values: &[Expr], -) -> FormatResult<()> { - write!(f, [literal(expr.range(), ContainsNewlines::Detect)])?; - write!(f, [end_of_line_comments(expr)])?; - Ok(()) -} - -fn format_constant( - f: &mut Formatter, - expr: &Expr, - constant: &Constant, - _kind: Option<&str>, -) -> FormatResult<()> { - match constant { - Constant::Ellipsis => write!(f, [text("...")])?, - Constant::None => write!(f, [text("None")])?, - Constant::Bool(value) => { - if *value { - write!(f, [text("True")])?; - } else { - write!(f, [text("False")])?; - } - } - Constant::Int(_) => write!(f, [int_literal(expr.range())])?, - Constant::Float(_) => write!(f, [float_literal(expr.range())])?, - Constant::Str(_) => write!(f, [string_literal(expr)])?, - Constant::Bytes(_) => write!(f, [string_literal(expr)])?, - Constant::Complex { .. } => write!(f, [complex_literal(expr.range())])?, - Constant::Tuple(_) => unreachable!("Constant::Tuple should be handled by format_tuple"), - } - write!(f, [end_of_line_comments(expr)])?; - Ok(()) -} - -fn format_dict( - f: &mut Formatter, - expr: &Expr, - keys: &[Option], - values: &[Expr], -) -> FormatResult<()> { - write!(f, [text("{")])?; - if !keys.is_empty() { - let magic_trailing_comma = expr.trivia.iter().any(|c| c.kind.is_magic_trailing_comma()); - write!( - f, - [soft_block_indent(&format_with(|f| { - if magic_trailing_comma { - write!(f, [expand_parent()])?; - } - for (i, (k, v)) in keys.iter().zip(values).enumerate() { - if let Some(k) = k { - write!(f, [k.format()])?; - write!(f, [text(":")])?; - write!(f, [space()])?; - if is_self_closing(v) { - write!(f, [v.format()])?; - } else { - write!( - f, - [group(&format_args![ - if_group_breaks(&text("(")), - soft_block_indent(&format_args![v.format()]), - if_group_breaks(&text(")")), - ])] - )?; - } - } else { - write!(f, [text("**")])?; - if is_self_closing(v) { - write!(f, [v.format()])?; - } else { - write!( - f, - [group(&format_args![ - if_group_breaks(&text("(")), - soft_block_indent(&format_args![v.format()]), - if_group_breaks(&text(")")), - ])] - )?; - } - } - if i < keys.len() - 1 { - write!(f, [text(",")])?; - write!(f, [soft_line_break_or_space()])?; - } else { - write!(f, [if_group_breaks(&text(","))])?; - } - } - Ok(()) - }))] - )?; - } - write!(f, [text("}")])?; - Ok(()) -} - -fn format_attribute( - f: &mut Formatter, - expr: &Expr, - value: &Expr, - attr: &str, -) -> FormatResult<()> { - write!(f, [value.format()])?; - write!(f, [text(".")])?; - write!(f, [dynamic_text(attr, None)])?; - write!(f, [end_of_line_comments(expr)])?; - Ok(()) -} - -fn format_named_expr( - f: &mut Formatter, - expr: &Expr, - target: &Expr, - value: &Expr, -) -> FormatResult<()> { - write!(f, [target.format()])?; - write!(f, [text(":=")])?; - write!(f, [space()])?; - write!(f, [group(&format_args![value.format()])])?; - write!(f, [end_of_line_comments(expr)])?; - Ok(()) -} - -fn format_bool_op( - f: &mut Formatter, - expr: &Expr, - ops: &[BoolOp], - values: &[Expr], -) -> FormatResult<()> { - write!(f, [group(&format_args![values[0].format()])])?; - for (op, value) in ops.iter().zip(&values[1..]) { - write!(f, [soft_line_break_or_space()])?; - write!(f, [op.format()])?; - write!(f, [space()])?; - write!(f, [group(&format_args![value.format()])])?; - } - write!(f, [end_of_line_comments(expr)])?; - Ok(()) -} - -fn format_bin_op( - f: &mut Formatter, - expr: &Expr, - left: &Expr, - op: &Operator, - right: &Expr, -) -> FormatResult<()> { - // https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#line-breaks-binary-operators - let is_simple = - matches!(op.node, OperatorKind::Pow) && is_simple_power(left) && is_simple_power(right); - write!(f, [left.format()])?; - if !is_simple { - write!(f, [soft_line_break_or_space()])?; - } - write!(f, [op.format()])?; - if !is_simple { - write!(f, [space()])?; - } - write!(f, [group(&right.format())])?; - write!(f, [end_of_line_comments(expr)])?; - Ok(()) -} - -fn format_unary_op( - f: &mut Formatter, - expr: &Expr, - op: &UnaryOp, - operand: &Expr, -) -> FormatResult<()> { - write!(f, [op.format()])?; - // TODO(charlie): Do this in the normalization pass. - if !matches!(op.node, UnaryOpKind::Not) - && matches!( - operand.node, - ExprKind::BoolOp { .. } | ExprKind::Compare { .. } | ExprKind::BinOp { .. } - ) - { - let parenthesized = operand.parentheses.is_always(); - if !parenthesized { - write!(f, [text("(")])?; - } - write!(f, [operand.format()])?; - if !parenthesized { - write!(f, [text(")")])?; - } - } else { - write!(f, [operand.format()])?; - } - write!(f, [end_of_line_comments(expr)])?; - Ok(()) -} - -fn format_lambda( - f: &mut Formatter, - expr: &Expr, - args: &Arguments, - body: &Expr, -) -> FormatResult<()> { - write!(f, [text("lambda")])?; - if !args.args.is_empty() || args.kwarg.is_some() || args.vararg.is_some() { - write!(f, [space()])?; - write!(f, [args.format()])?; - } - write!(f, [text(":")])?; - write!(f, [space()])?; - write!(f, [body.format()])?; - write!(f, [end_of_line_comments(expr)])?; - Ok(()) -} - -fn format_if_exp( - f: &mut Formatter, - expr: &Expr, - test: &Expr, - body: &Expr, - orelse: &Expr, -) -> FormatResult<()> { - write!(f, [group(&format_args![body.format()])])?; - write!(f, [soft_line_break_or_space()])?; - write!(f, [text("if")])?; - write!(f, [space()])?; - write!(f, [group(&format_args![test.format()])])?; - write!(f, [soft_line_break_or_space()])?; - write!(f, [text("else")])?; - write!(f, [space()])?; - write!(f, [group(&format_args![orelse.format()])])?; - Ok(()) -} - -impl Format> for FormatExpr<'_> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - if self.item.parentheses.is_always() { - write!(f, [text("(")])?; - } - - write!(f, [leading_comments(self.item)])?; - - match &self.item.node { - ExprKind::BoolOp { ops, values } => format_bool_op(f, self.item, ops, values), - ExprKind::NamedExpr { target, value } => format_named_expr(f, self.item, target, value), - ExprKind::BinOp { left, op, right } => format_bin_op(f, self.item, left, op, right), - ExprKind::UnaryOp { op, operand } => format_unary_op(f, self.item, op, operand), - ExprKind::Lambda { args, body } => format_lambda(f, self.item, args, body), - ExprKind::IfExp { test, body, orelse } => { - format_if_exp(f, self.item, test, body, orelse) - } - ExprKind::Dict { keys, values } => format_dict(f, self.item, keys, values), - ExprKind::Set { elts, .. } => format_set(f, self.item, elts), - ExprKind::ListComp { elt, generators } => { - format_list_comp(f, self.item, elt, generators) - } - ExprKind::SetComp { elt, generators } => format_set_comp(f, self.item, elt, generators), - ExprKind::DictComp { - key, - value, - generators, - } => format_dict_comp(f, self.item, key, value, generators), - ExprKind::GeneratorExp { elt, generators } => { - format_generator_exp(f, self.item, elt, generators) - } - ExprKind::Await { value } => format_await(f, self.item, value), - ExprKind::Yield { value } => format_yield(f, self.item, value.as_deref()), - ExprKind::YieldFrom { value } => format_yield_from(f, self.item, value), - ExprKind::Compare { - left, - ops, - comparators, - } => format_compare(f, self.item, left, ops, comparators), - ExprKind::Call { - func, - args, - keywords, - } => format_call(f, self.item, func, args, keywords), - ExprKind::JoinedStr { values } => format_joined_str(f, self.item, values), - ExprKind::Constant { value, kind } => { - format_constant(f, self.item, value, kind.as_deref()) - } - ExprKind::Attribute { value, attr, .. } => format_attribute(f, self.item, value, attr), - ExprKind::Subscript { value, slice, .. } => { - format_subscript(f, self.item, value, slice) - } - ExprKind::Starred { value, .. } => format_starred(f, self.item, value), - ExprKind::Name { id, .. } => format_name(f, self.item, id), - ExprKind::List { elts, .. } => format_list(f, self.item, elts), - ExprKind::Tuple { elts, .. } => format_tuple(f, self.item, elts), - ExprKind::Slice { lower, upper, step } => { - format_slice(f, self.item, lower, upper, step.as_ref()) - } - ExprKind::FormattedValue { - value, - conversion, - format_spec, - } => format_formatted_value(f, self.item, value, *conversion, format_spec.as_deref()), - }?; - - // Any trailing comments come on the lines after. - for trivia in &self.item.trivia { - if trivia.relationship.is_trailing() { - if let TriviaKind::OwnLineComment(range) = trivia.kind { - write!(f, [expand_parent()])?; - write!(f, [literal(range, ContainsNewlines::No)])?; - write!(f, [hard_line_break()])?; - } - } - } - - if self.item.parentheses.is_always() { - write!(f, [text(")")])?; - } - - Ok(()) - } -} - -impl AsFormat> for Expr { - type Format<'a> = FormatExpr<'a>; - - fn format(&self) -> Self::Format<'_> { - FormatExpr { item: self } - } -} diff --git a/crates/ruff_python_formatter/src/format/helpers.rs b/crates/ruff_python_formatter/src/format/helpers.rs deleted file mode 100644 index 6678dc3dc974e..0000000000000 --- a/crates/ruff_python_formatter/src/format/helpers.rs +++ /dev/null @@ -1,87 +0,0 @@ -use crate::cst::{Expr, ExprKind, UnaryOpKind}; - -pub(crate) fn is_self_closing(expr: &Expr) -> bool { - match &expr.node { - ExprKind::Tuple { .. } - | ExprKind::List { .. } - | ExprKind::Set { .. } - | ExprKind::Dict { .. } - | ExprKind::ListComp { .. } - | ExprKind::SetComp { .. } - | ExprKind::DictComp { .. } - | ExprKind::GeneratorExp { .. } - | ExprKind::Call { .. } - | ExprKind::Name { .. } - | ExprKind::Constant { .. } - | ExprKind::Subscript { .. } => true, - ExprKind::Lambda { body, .. } => is_self_closing(body), - ExprKind::BinOp { left, right, .. } => { - matches!(left.node, ExprKind::Constant { .. } | ExprKind::Name { .. }) - && matches!( - right.node, - ExprKind::Tuple { .. } - | ExprKind::List { .. } - | ExprKind::Set { .. } - | ExprKind::Dict { .. } - | ExprKind::ListComp { .. } - | ExprKind::SetComp { .. } - | ExprKind::DictComp { .. } - | ExprKind::GeneratorExp { .. } - | ExprKind::Call { .. } - | ExprKind::Subscript { .. } - ) - } - ExprKind::BoolOp { values, .. } => values.last().map_or(false, |expr| { - matches!( - expr.node, - ExprKind::Tuple { .. } - | ExprKind::List { .. } - | ExprKind::Set { .. } - | ExprKind::Dict { .. } - | ExprKind::ListComp { .. } - | ExprKind::SetComp { .. } - | ExprKind::DictComp { .. } - | ExprKind::GeneratorExp { .. } - | ExprKind::Call { .. } - | ExprKind::Subscript { .. } - ) - }), - ExprKind::UnaryOp { operand, .. } => is_self_closing(operand), - _ => false, - } -} - -/// Return `true` if an [`Expr`] adheres to Black's definition of a non-complex -/// expression, in the context of a slice operation. -pub(crate) fn is_simple_slice(expr: &Expr) -> bool { - match &expr.node { - ExprKind::UnaryOp { op, operand } => { - if matches!(op.node, UnaryOpKind::Not) { - false - } else { - is_simple_slice(operand) - } - } - ExprKind::Constant { .. } => true, - ExprKind::Name { .. } => true, - _ => false, - } -} - -/// Return `true` if an [`Expr`] adheres to Black's definition of a non-complex -/// expression, in the context of a power operation. -pub(crate) fn is_simple_power(expr: &Expr) -> bool { - match &expr.node { - ExprKind::UnaryOp { op, operand } => { - if matches!(op.node, UnaryOpKind::Not) { - false - } else { - is_simple_slice(operand) - } - } - ExprKind::Constant { .. } => true, - ExprKind::Name { .. } => true, - ExprKind::Attribute { value, .. } => is_simple_power(value), - _ => false, - } -} diff --git a/crates/ruff_python_formatter/src/format/keyword.rs b/crates/ruff_python_formatter/src/format/keyword.rs deleted file mode 100644 index c277b7de57c38..0000000000000 --- a/crates/ruff_python_formatter/src/format/keyword.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::prelude::*; -use ruff_formatter::write; - -use crate::cst::Keyword; -use crate::format::comments::{end_of_line_comments, leading_comments, trailing_comments}; - -pub(crate) struct FormatKeyword<'a> { - item: &'a Keyword, -} - -impl AsFormat> for Keyword { - type Format<'a> = FormatKeyword<'a>; - - fn format(&self) -> Self::Format<'_> { - FormatKeyword { item: self } - } -} - -impl Format> for FormatKeyword<'_> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let keyword = self.item; - - write!(f, [leading_comments(keyword)])?; - if let Some(arg) = &keyword.arg { - write!(f, [dynamic_text(arg, None)])?; - write!(f, [text("=")])?; - write!(f, [keyword.value.format()])?; - } else { - write!(f, [text("**")])?; - write!(f, [keyword.value.format()])?; - } - write!(f, [end_of_line_comments(keyword)])?; - write!(f, [trailing_comments(keyword)])?; - - Ok(()) - } -} diff --git a/crates/ruff_python_formatter/src/format/match_case.rs b/crates/ruff_python_formatter/src/format/match_case.rs deleted file mode 100644 index d71ac3c919620..0000000000000 --- a/crates/ruff_python_formatter/src/format/match_case.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::prelude::*; -use ruff_formatter::write; - -use crate::cst::MatchCase; -use crate::format::builders::block; -use crate::format::comments::{end_of_line_comments, leading_comments}; - -pub(crate) struct FormatMatchCase<'a> { - item: &'a MatchCase, -} - -impl AsFormat> for MatchCase { - type Format<'a> = FormatMatchCase<'a>; - - fn format(&self) -> Self::Format<'_> { - FormatMatchCase { item: self } - } -} - -impl Format> for FormatMatchCase<'_> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let MatchCase { - pattern, - guard, - body, - } = self.item; - - write!(f, [leading_comments(pattern)])?; - - write!(f, [text("case")])?; - write!(f, [space(), pattern.format()])?; - if let Some(guard) = &guard { - write!(f, [space(), text("if"), space(), guard.format()])?; - } - write!(f, [text(":")])?; - - write!(f, [end_of_line_comments(body)])?; - write!(f, [block_indent(&block(body))])?; - - Ok(()) - } -} diff --git a/crates/ruff_python_formatter/src/format/mod.rs b/crates/ruff_python_formatter/src/format/mod.rs deleted file mode 100644 index 4605718f0e0fa..0000000000000 --- a/crates/ruff_python_formatter/src/format/mod.rs +++ /dev/null @@ -1,20 +0,0 @@ -mod alias; -mod arg; -mod arguments; -mod bool_op; -pub(crate) mod builders; -mod cmp_op; -mod comments; -mod comprehension; -mod excepthandler; -mod expr; -mod helpers; -mod keyword; -mod match_case; -mod numbers; -mod operator; -mod pattern; -mod stmt; -mod strings; -mod unary_op; -mod withitem; diff --git a/crates/ruff_python_formatter/src/format/numbers.rs b/crates/ruff_python_formatter/src/format/numbers.rs deleted file mode 100644 index dde5a1c5371d7..0000000000000 --- a/crates/ruff_python_formatter/src/format/numbers.rs +++ /dev/null @@ -1,203 +0,0 @@ -use std::ops::{Add, Sub}; - -use crate::prelude::*; -use ruff_formatter::{write, Format}; -use ruff_text_size::{TextRange, TextSize}; - -use crate::format::builders::literal; - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -struct FloatAtom { - range: TextRange, -} - -impl Format> for FloatAtom { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let contents = f.context().contents(); - - let content = &contents[self.range]; - if let Some(dot_index) = content.find('.') { - let integer = &content[..dot_index]; - let fractional = &content[dot_index + 1..]; - - if integer.is_empty() { - write!(f, [text("0")])?; - } else { - write!( - f, - [literal( - TextRange::new( - self.range.start(), - self.range - .start() - .add(TextSize::try_from(dot_index).unwrap()) - ), - ContainsNewlines::No - )] - )?; - } - - write!(f, [text(".")])?; - - if fractional.is_empty() { - write!(f, [text("0")])?; - } else { - write!( - f, - [literal( - TextRange::new( - self.range - .start() - .add(TextSize::try_from(dot_index + 1).unwrap()), - self.range.end() - ), - ContainsNewlines::No - )] - )?; - } - } else { - write!(f, [literal(self.range, ContainsNewlines::No)])?; - } - - Ok(()) - } -} - -#[inline] -const fn float_atom(range: TextRange) -> FloatAtom { - FloatAtom { range } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub(crate) struct FloatLiteral { - range: TextRange, -} - -impl Format> for FloatLiteral { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let contents = f.context().contents(); - - let content = &contents[self.range]; - - // Scientific notation - if let Some(exponent_index) = content.find('e').or_else(|| content.find('E')) { - // Write the base. - write!( - f, - [float_atom(TextRange::new( - self.range.start(), - self.range - .start() - .add(TextSize::try_from(exponent_index).unwrap()) - ))] - )?; - - write!(f, [text("e")])?; - - // Write the exponent, omitting the sign if it's positive. - let plus = content[exponent_index + 1..].starts_with('+'); - write!( - f, - [literal( - TextRange::new( - self.range.start().add( - TextSize::try_from(exponent_index + 1 + usize::from(plus)).unwrap() - ), - self.range.end() - ), - ContainsNewlines::No - )] - )?; - } else { - write!(f, [float_atom(self.range)])?; - } - - Ok(()) - } -} - -#[inline] -pub(crate) const fn float_literal(range: TextRange) -> FloatLiteral { - FloatLiteral { range } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub(crate) struct IntLiteral { - range: TextRange, -} - -impl Format> for IntLiteral { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let contents = f.context().contents(); - - for prefix in ["0b", "0B", "0o", "0O", "0x", "0X"] { - let content = &contents[self.range]; - if content.starts_with(prefix) { - // In each case, the prefix must be lowercase, while the suffix must be uppercase. - let prefix = &content[..prefix.len()]; - let suffix = &content[prefix.len()..]; - - if prefix.bytes().any(|b| b.is_ascii_uppercase()) - || suffix.bytes().any(|b| b.is_ascii_lowercase()) - { - // Write out the fixed version. - write!( - f, - [ - dynamic_text(&prefix.to_lowercase(), None), - dynamic_text(&suffix.to_uppercase(), None) - ] - )?; - } else { - // Use the existing source. - write!(f, [literal(self.range, ContainsNewlines::No)])?; - } - - return Ok(()); - } - } - - write!(f, [literal(self.range, ContainsNewlines::No)])?; - - Ok(()) - } -} - -#[inline] -pub(crate) const fn int_literal(range: TextRange) -> IntLiteral { - IntLiteral { range } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub(crate) struct ComplexLiteral { - range: TextRange, -} - -impl Format> for ComplexLiteral { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let contents = f.context().contents(); - let content = &contents[self.range]; - - if content.ends_with('j') { - write!(f, [literal(self.range, ContainsNewlines::No)])?; - } else if content.ends_with('J') { - write!( - f, - [literal( - TextRange::new(self.range.start(), self.range.end().sub(TextSize::from(1))), - ContainsNewlines::No - )] - )?; - write!(f, [text("j")])?; - } else { - unreachable!("expected complex literal to end with j or J"); - } - - Ok(()) - } -} - -#[inline] -pub(crate) const fn complex_literal(range: TextRange) -> ComplexLiteral { - ComplexLiteral { range } -} diff --git a/crates/ruff_python_formatter/src/format/operator.rs b/crates/ruff_python_formatter/src/format/operator.rs deleted file mode 100644 index 360cea69cbe45..0000000000000 --- a/crates/ruff_python_formatter/src/format/operator.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::prelude::*; -use ruff_formatter::write; - -use crate::cst::{Operator, OperatorKind}; -use crate::format::comments::{end_of_line_comments, leading_comments, trailing_comments}; - -pub(crate) struct FormatOperator<'a> { - item: &'a Operator, -} - -impl AsFormat> for Operator { - type Format<'a> = FormatOperator<'a>; - - fn format(&self) -> Self::Format<'_> { - FormatOperator { item: self } - } -} - -impl Format> for FormatOperator<'_> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let operator = self.item; - write!(f, [leading_comments(operator)])?; - write!( - f, - [text(match operator.node { - OperatorKind::Add => "+", - OperatorKind::Sub => "-", - OperatorKind::Mult => "*", - OperatorKind::MatMult => "@", - OperatorKind::Div => "/", - OperatorKind::Mod => "%", - OperatorKind::Pow => "**", - OperatorKind::LShift => "<<", - OperatorKind::RShift => ">>", - OperatorKind::BitOr => "|", - OperatorKind::BitXor => "^", - OperatorKind::BitAnd => "&", - OperatorKind::FloorDiv => "//", - })] - )?; - write!(f, [end_of_line_comments(operator)])?; - write!(f, [trailing_comments(operator)])?; - Ok(()) - } -} diff --git a/crates/ruff_python_formatter/src/format/pattern.rs b/crates/ruff_python_formatter/src/format/pattern.rs deleted file mode 100644 index 565312c92e9d2..0000000000000 --- a/crates/ruff_python_formatter/src/format/pattern.rs +++ /dev/null @@ -1,153 +0,0 @@ -use rustpython_parser::ast::Constant; - -use crate::prelude::*; -use ruff_formatter::write; - -use crate::cst::{Pattern, PatternKind}; - -pub(crate) struct FormatPattern<'a> { - item: &'a Pattern, -} - -impl AsFormat> for Pattern { - type Format<'a> = FormatPattern<'a>; - - fn format(&self) -> Self::Format<'_> { - FormatPattern { item: self } - } -} - -impl Format> for FormatPattern<'_> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let pattern = self.item; - - match &pattern.node { - PatternKind::MatchValue { value } => { - write!(f, [value.format()])?; - } - PatternKind::MatchSingleton { value } => match value { - Constant::None => write!(f, [text("None")])?, - Constant::Bool(value) => { - if *value { - write!(f, [text("True")])?; - } else { - write!(f, [text("False")])?; - } - } - _ => unreachable!("singleton pattern must be None or bool"), - }, - PatternKind::MatchSequence { patterns } => { - write!(f, [text("[")])?; - if let Some(pattern) = patterns.first() { - write!(f, [pattern.format()])?; - } - for pattern in patterns.iter().skip(1) { - write!(f, [text(","), space(), pattern.format()])?; - } - write!(f, [text("]")])?; - } - PatternKind::MatchMapping { - keys, - patterns, - rest, - } => { - write!(f, [text("{")])?; - if let Some(pattern) = patterns.first() { - write!(f, [keys[0].format(), text(":"), space(), pattern.format()])?; - } - for (key, pattern) in keys.iter().skip(1).zip(patterns.iter().skip(1)) { - write!( - f, - [ - text(","), - space(), - key.format(), - text(":"), - space(), - pattern.format() - ] - )?; - } - if let Some(rest) = &rest { - write!( - f, - [ - text(","), - space(), - text("**"), - space(), - dynamic_text(rest, None) - ] - )?; - } - write!(f, [text("}")])?; - } - PatternKind::MatchClass { - cls, - patterns, - kwd_attrs, - kwd_patterns, - } => { - write!(f, [cls.format()])?; - if !patterns.is_empty() { - write!(f, [text("(")])?; - if let Some(pattern) = patterns.first() { - write!(f, [pattern.format()])?; - } - for pattern in patterns.iter().skip(1) { - write!(f, [text(","), space(), pattern.format()])?; - } - write!(f, [text(")")])?; - } - if !kwd_attrs.is_empty() { - write!(f, [text("(")])?; - if let Some(attr) = kwd_attrs.first() { - write!(f, [dynamic_text(attr, None)])?; - } - for attr in kwd_attrs.iter().skip(1) { - write!(f, [text(","), space(), dynamic_text(attr, None)])?; - } - write!(f, [text(")")])?; - } - if !kwd_patterns.is_empty() { - write!(f, [text("(")])?; - if let Some(pattern) = kwd_patterns.first() { - write!(f, [pattern.format()])?; - } - for pattern in kwd_patterns.iter().skip(1) { - write!(f, [text(","), space(), pattern.format()])?; - } - write!(f, [text(")")])?; - } - } - PatternKind::MatchStar { name } => { - if let Some(name) = name { - write!(f, [text("*"), dynamic_text(name, None)])?; - } else { - write!(f, [text("*_")])?; - } - } - PatternKind::MatchAs { pattern, name } => { - if let Some(pattern) = &pattern { - write!(f, [pattern.format()])?; - write!(f, [space()])?; - write!(f, [text("as")])?; - write!(f, [space()])?; - } - if let Some(name) = name { - write!(f, [dynamic_text(name, None)])?; - } else { - write!(f, [text("_")])?; - } - } - PatternKind::MatchOr { patterns } => { - write!(f, [patterns[0].format()])?; - for pattern in patterns.iter().skip(1) { - write!(f, [space(), text("|"), space(), pattern.format()])?; - } - } - } - - Ok(()) - } -} diff --git a/crates/ruff_python_formatter/src/format/stmt.rs b/crates/ruff_python_formatter/src/format/stmt.rs deleted file mode 100644 index 0e76023b01b59..0000000000000 --- a/crates/ruff_python_formatter/src/format/stmt.rs +++ /dev/null @@ -1,946 +0,0 @@ -#![allow(unused_variables, clippy::too_many_arguments)] - -use crate::prelude::*; -use ruff_formatter::{format_args, write}; - -use crate::cst::{ - Alias, Arguments, Body, Excepthandler, Expr, ExprKind, Keyword, MatchCase, Operator, Stmt, - StmtKind, Withitem, -}; -use crate::format::builders::{block, join_names}; -use crate::format::comments::{end_of_line_comments, leading_comments, trailing_comments}; -use crate::format::helpers::is_self_closing; - -fn format_break(f: &mut Formatter, stmt: &Stmt) -> FormatResult<()> { - write!(f, [text("break")])?; - write!(f, [end_of_line_comments(stmt)])?; - Ok(()) -} - -fn format_pass(f: &mut Formatter, stmt: &Stmt) -> FormatResult<()> { - write!(f, [text("pass")])?; - write!(f, [end_of_line_comments(stmt)])?; - Ok(()) -} - -fn format_continue(f: &mut Formatter, stmt: &Stmt) -> FormatResult<()> { - write!(f, [text("continue")])?; - write!(f, [end_of_line_comments(stmt)])?; - Ok(()) -} - -fn format_global( - f: &mut Formatter, - stmt: &Stmt, - names: &[String], -) -> FormatResult<()> { - write!(f, [text("global")])?; - if !names.is_empty() { - write!(f, [space(), join_names(names)])?; - } - write!(f, [end_of_line_comments(stmt)])?; - Ok(()) -} - -fn format_nonlocal( - f: &mut Formatter, - stmt: &Stmt, - names: &[String], -) -> FormatResult<()> { - write!(f, [text("nonlocal")])?; - if !names.is_empty() { - write!(f, [space(), join_names(names)])?; - } - write!(f, [end_of_line_comments(stmt)])?; - Ok(()) -} - -fn format_delete( - f: &mut Formatter, - stmt: &Stmt, - targets: &[Expr], -) -> FormatResult<()> { - write!(f, [text("del")])?; - match targets.len() { - 0 => {} - 1 => write!(f, [space(), targets[0].format()])?, - _ => { - write!( - f, - [ - space(), - group(&format_args![ - if_group_breaks(&text("(")), - soft_block_indent(&format_with(|f| { - for (i, target) in targets.iter().enumerate() { - write!(f, [target.format()])?; - - if i < targets.len() - 1 { - write!(f, [text(","), soft_line_break_or_space()])?; - } else { - write!(f, [if_group_breaks(&text(","))])?; - } - } - Ok(()) - })), - if_group_breaks(&text(")")), - ]) - ] - )?; - } - } - write!(f, [end_of_line_comments(stmt)])?; - Ok(()) -} - -fn format_class_def( - f: &mut Formatter, - name: &str, - bases: &[Expr], - keywords: &[Keyword], - body: &Body, - decorator_list: &[Expr], -) -> FormatResult<()> { - for decorator in decorator_list { - write!(f, [text("@"), decorator.format(), hard_line_break()])?; - } - - write!(f, [leading_comments(body)])?; - - write!(f, [text("class"), space(), dynamic_text(name, None)])?; - - if !bases.is_empty() || !keywords.is_empty() { - let format_bases = format_with(|f| { - for (i, expr) in bases.iter().enumerate() { - write!(f, [expr.format()])?; - - if i < bases.len() - 1 || !keywords.is_empty() { - write!(f, [text(","), soft_line_break_or_space()])?; - } else { - write!(f, [if_group_breaks(&text(","))])?; - } - - for (i, keyword) in keywords.iter().enumerate() { - write!(f, [keyword.format()])?; - if i < keywords.len() - 1 { - write!(f, [text(","), soft_line_break_or_space()])?; - } else { - write!(f, [if_group_breaks(&text(","))])?; - } - } - } - Ok(()) - }); - - write!( - f, - [ - text("("), - group(&soft_block_indent(&format_bases)), - text(")") - ] - )?; - } - - write!(f, [end_of_line_comments(body)])?; - write!(f, [text(":"), block_indent(&block(body))]) -} - -fn format_func_def( - f: &mut Formatter, - stmt: &Stmt, - name: &str, - args: &Arguments, - returns: Option<&Expr>, - body: &Body, - decorator_list: &[Expr], - async_: bool, -) -> FormatResult<()> { - for decorator in decorator_list { - write!(f, [text("@"), decorator.format(), hard_line_break()])?; - } - - write!(f, [leading_comments(body)])?; - - if async_ { - write!(f, [text("async"), space()])?; - } - write!( - f, - [ - text("def"), - space(), - dynamic_text(name, None), - text("("), - group(&soft_block_indent(&format_with(|f| { - if stmt.trivia.iter().any(|c| c.kind.is_magic_trailing_comma()) { - write!(f, [expand_parent()])?; - } - write!(f, [args.format()]) - }))), - text(")") - ] - )?; - - if let Some(returns) = returns { - write!(f, [text(" -> "), returns.format()])?; - } - - write!(f, [text(":")])?; - write!(f, [end_of_line_comments(body)])?; - write!(f, [block_indent(&block(body))])?; - - Ok(()) -} - -fn format_assign( - f: &mut Formatter, - stmt: &Stmt, - targets: &[Expr], - value: &Expr, -) -> FormatResult<()> { - write!(f, [targets[0].format()])?; - - for target in &targets[1..] { - // TODO(charlie): This doesn't match Black's behavior. We need to parenthesize - // this expression sometimes. - write!(f, [text(" = "), target.format()])?; - } - write!(f, [text(" = ")])?; - if is_self_closing(value) { - write!(f, [group(&value.format())])?; - } else { - write!( - f, - [group(&format_args![ - if_group_breaks(&text("(")), - soft_block_indent(&value.format()), - if_group_breaks(&text(")")), - ])] - )?; - } - - write!(f, [end_of_line_comments(stmt)])?; - - Ok(()) -} - -fn format_aug_assign( - f: &mut Formatter, - stmt: &Stmt, - target: &Expr, - op: &Operator, - value: &Expr, -) -> FormatResult<()> { - write!(f, [target.format()])?; - write!(f, [text(" "), op.format(), text("=")])?; - if is_self_closing(value) { - write!(f, [space(), group(&value.format())])?; - } else { - write!( - f, - [ - space(), - group(&format_args![ - if_group_breaks(&text("(")), - soft_block_indent(&value.format()), - if_group_breaks(&text(")")), - ]) - ] - )?; - } - write!(f, [end_of_line_comments(stmt)])?; - Ok(()) -} - -fn format_ann_assign( - f: &mut Formatter, - stmt: &Stmt, - target: &Expr, - annotation: &Expr, - value: Option<&Expr>, - simple: usize, -) -> FormatResult<()> { - let need_parens = matches!(target.node, ExprKind::Name { .. }) && simple == 0; - if need_parens { - write!(f, [text("(")])?; - } - write!(f, [target.format()])?; - if need_parens { - write!(f, [text(")")])?; - } - write!(f, [text(": "), annotation.format()])?; - - if let Some(value) = value { - write!( - f, - [ - space(), - text("="), - space(), - group(&format_args![ - if_group_breaks(&text("(")), - soft_block_indent(&value.format()), - if_group_breaks(&text(")")), - ]) - ] - )?; - } - - Ok(()) -} - -fn format_for( - f: &mut Formatter, - stmt: &Stmt, - target: &Expr, - iter: &Expr, - body: &Body, - orelse: Option<&Body>, - _type_comment: Option<&str>, - async_: bool, -) -> FormatResult<()> { - if async_ { - write!(f, [text("async"), space()])?; - } - write!( - f, - [ - text("for"), - space(), - group(&target.format()), - space(), - text("in"), - space(), - group(&iter.format()), - text(":"), - end_of_line_comments(body), - block_indent(&block(body)) - ] - )?; - if let Some(orelse) = orelse { - write!( - f, - [ - text("else:"), - end_of_line_comments(orelse), - block_indent(&block(orelse)) - ] - )?; - } - Ok(()) -} - -fn format_while( - f: &mut Formatter, - stmt: &Stmt, - test: &Expr, - body: &Body, - orelse: Option<&Body>, -) -> FormatResult<()> { - write!(f, [text("while"), space()])?; - if is_self_closing(test) { - write!(f, [test.format()])?; - } else { - write!( - f, - [group(&format_args![ - if_group_breaks(&text("(")), - soft_block_indent(&test.format()), - if_group_breaks(&text(")")), - ])] - )?; - } - write!( - f, - [ - text(":"), - end_of_line_comments(body), - block_indent(&block(body)) - ] - )?; - if let Some(orelse) = orelse { - write!( - f, - [ - text("else:"), - end_of_line_comments(orelse), - block_indent(&block(orelse)) - ] - )?; - } - Ok(()) -} - -fn format_if( - f: &mut Formatter, - test: &Expr, - body: &Body, - orelse: Option<&Body>, - is_elif: bool, -) -> FormatResult<()> { - if is_elif { - write!(f, [text("elif"), space()])?; - } else { - write!(f, [text("if"), space()])?; - } - if is_self_closing(test) { - write!(f, [test.format()])?; - } else { - write!( - f, - [group(&format_args![ - if_group_breaks(&text("(")), - soft_block_indent(&test.format()), - if_group_breaks(&text(")")), - ])] - )?; - } - write!( - f, - [ - text(":"), - end_of_line_comments(body), - block_indent(&block(body)) - ] - )?; - if let Some(orelse) = orelse { - if orelse.node.len() == 1 { - if let StmtKind::If { - test, - body, - orelse, - is_elif: true, - } = &orelse.node[0].node - { - format_if(f, test, body, orelse.as_ref(), true)?; - } else { - write!( - f, - [ - text("else:"), - end_of_line_comments(orelse), - block_indent(&block(orelse)) - ] - )?; - } - } else { - write!( - f, - [ - text("else:"), - end_of_line_comments(orelse), - block_indent(&block(orelse)) - ] - )?; - } - } - Ok(()) -} - -fn format_match( - f: &mut Formatter, - stmt: &Stmt, - subject: &Expr, - cases: &[MatchCase], -) -> FormatResult<()> { - write!( - f, - [ - text("match"), - space(), - subject.format(), - text(":"), - end_of_line_comments(stmt), - ] - )?; - for case in cases { - write!(f, [block_indent(&case.format())])?; - } - Ok(()) -} - -fn format_raise( - f: &mut Formatter, - stmt: &Stmt, - exc: Option<&Expr>, - cause: Option<&Expr>, -) -> FormatResult<()> { - write!(f, [text("raise")])?; - if let Some(exc) = exc { - write!(f, [space(), exc.format()])?; - if let Some(cause) = cause { - write!(f, [space(), text("from"), space(), cause.format()])?; - } - } - Ok(()) -} - -fn format_return( - f: &mut Formatter, - stmt: &Stmt, - value: Option<&Expr>, -) -> FormatResult<()> { - write!(f, [text("return")])?; - if let Some(value) = value { - write!(f, [space(), value.format()])?; - } - - write!(f, [end_of_line_comments(stmt)])?; - - Ok(()) -} - -fn format_try( - f: &mut Formatter, - stmt: &Stmt, - body: &Body, - handlers: &[Excepthandler], - orelse: Option<&Body>, - finalbody: Option<&Body>, -) -> FormatResult<()> { - write!( - f, - [ - text("try:"), - end_of_line_comments(body), - block_indent(&block(body)) - ] - )?; - for handler in handlers { - write!(f, [handler.format()])?; - } - if let Some(orelse) = orelse { - write!(f, [text("else:")])?; - write!(f, [end_of_line_comments(orelse)])?; - write!(f, [block_indent(&block(orelse))])?; - } - if let Some(finalbody) = finalbody { - write!(f, [text("finally:")])?; - write!(f, [end_of_line_comments(finalbody)])?; - write!(f, [block_indent(&block(finalbody))])?; - } - Ok(()) -} - -fn format_try_star( - f: &mut Formatter, - stmt: &Stmt, - body: &Body, - handlers: &[Excepthandler], - orelse: Option<&Body>, - finalbody: Option<&Body>, -) -> FormatResult<()> { - write!( - f, - [ - text("try:"), - end_of_line_comments(body), - block_indent(&block(body)) - ] - )?; - for handler in handlers { - // TODO(charlie): Include `except*`. - write!(f, [handler.format()])?; - } - if let Some(orelse) = orelse { - write!( - f, - [ - text("else:"), - end_of_line_comments(orelse), - block_indent(&block(orelse)) - ] - )?; - } - if let Some(finalbody) = finalbody { - write!( - f, - [ - text("finally:"), - end_of_line_comments(finalbody), - block_indent(&block(finalbody)) - ] - )?; - } - Ok(()) -} - -fn format_assert( - f: &mut Formatter, - stmt: &Stmt, - test: &Expr, - msg: Option<&Expr>, -) -> FormatResult<()> { - write!(f, [text("assert"), space()])?; - write!( - f, - [group(&format_args![ - if_group_breaks(&text("(")), - soft_block_indent(&test.format()), - if_group_breaks(&text(")")), - ])] - )?; - if let Some(msg) = msg { - write!( - f, - [ - text(","), - space(), - group(&format_args![ - if_group_breaks(&text("(")), - soft_block_indent(&msg.format()), - if_group_breaks(&text(")")), - ]) - ] - )?; - } - Ok(()) -} - -fn format_import( - f: &mut Formatter, - stmt: &Stmt, - names: &[Alias], -) -> FormatResult<()> { - write!( - f, - [ - text("import"), - space(), - group(&format_args![ - if_group_breaks(&text("(")), - soft_block_indent(&format_with(|f| { - for (i, name) in names.iter().enumerate() { - write!(f, [name.format()])?; - if i < names.len() - 1 { - write!(f, [text(","), soft_line_break_or_space()])?; - } else { - write!(f, [if_group_breaks(&text(","))])?; - } - } - Ok(()) - })), - if_group_breaks(&text(")")), - ]) - ] - ) -} - -fn format_import_from( - f: &mut Formatter, - stmt: &Stmt, - module: Option<&str>, - names: &[Alias], - level: Option, -) -> FormatResult<()> { - write!(f, [text("from")])?; - write!(f, [space()])?; - - if let Some(level) = level { - for _ in 0..level { - write!(f, [text(".")])?; - } - } - if let Some(module) = module { - write!(f, [dynamic_text(module, None)])?; - } - write!(f, [space()])?; - - write!(f, [text("import")])?; - write!(f, [space()])?; - - if names.iter().any(|name| name.node.name == "*") { - write!(f, [text("*")])?; - } else { - let magic_trailing_comma = stmt.trivia.iter().any(|c| c.kind.is_magic_trailing_comma()); - write!( - f, - [group(&format_args![ - if_group_breaks(&text("(")), - soft_block_indent(&format_with(|f| { - if magic_trailing_comma { - write!(f, [expand_parent()])?; - } - for (i, name) in names.iter().enumerate() { - write!(f, [name.format()])?; - if i < names.len() - 1 { - write!(f, [text(",")])?; - write!(f, [soft_line_break_or_space()])?; - } else { - write!(f, [if_group_breaks(&text(","))])?; - } - } - Ok(()) - })), - if_group_breaks(&text(")")), - ])] - )?; - } - - write!(f, [end_of_line_comments(stmt)])?; - - Ok(()) -} - -fn format_expr(f: &mut Formatter, stmt: &Stmt, expr: &Expr) -> FormatResult<()> { - if stmt.parentheses.is_always() { - write!( - f, - [group(&format_args![ - text("("), - soft_block_indent(&format_args![expr.format()]), - text(")"), - ])] - )?; - } else if is_self_closing(expr) { - write!(f, [group(&format_args![expr.format()])])?; - } else { - write!( - f, - [group(&format_args![ - if_group_breaks(&text("(")), - soft_block_indent(&format_args![expr.format()]), - if_group_breaks(&text(")")), - ])] - )?; - } - - write!(f, [end_of_line_comments(stmt)])?; - - Ok(()) -} - -fn format_with_( - f: &mut Formatter, - stmt: &Stmt, - items: &[Withitem], - body: &Body, - type_comment: Option<&str>, - async_: bool, -) -> FormatResult<()> { - if async_ { - write!(f, [text("async"), space()])?; - } - write!( - f, - [ - text("with"), - space(), - group(&format_args![ - if_group_breaks(&text("(")), - soft_block_indent(&format_with(|f| { - for (i, item) in items.iter().enumerate() { - write!(f, [item.format()])?; - if i < items.len() - 1 { - write!(f, [text(","), soft_line_break_or_space()])?; - } else { - write!(f, [if_group_breaks(&text(","))])?; - } - } - Ok(()) - })), - if_group_breaks(&text(")")), - ]), - text(":"), - end_of_line_comments(body), - block_indent(&block(body)) - ] - )?; - Ok(()) -} - -pub(crate) struct FormatStmt<'a> { - item: &'a Stmt, -} - -impl Format> for FormatStmt<'_> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - write!(f, [leading_comments(self.item)])?; - - match &self.item.node { - StmtKind::Pass => format_pass(f, self.item), - StmtKind::Break => format_break(f, self.item), - StmtKind::Continue => format_continue(f, self.item), - StmtKind::Global { names } => format_global(f, self.item, names), - StmtKind::Nonlocal { names } => format_nonlocal(f, self.item, names), - StmtKind::FunctionDef { - name, - args, - body, - decorator_list, - returns, - .. - } => format_func_def( - f, - self.item, - name, - args, - returns.as_deref(), - body, - decorator_list, - false, - ), - StmtKind::AsyncFunctionDef { - name, - args, - body, - decorator_list, - returns, - .. - } => format_func_def( - f, - self.item, - name, - args, - returns.as_deref(), - body, - decorator_list, - true, - ), - StmtKind::ClassDef { - name, - bases, - keywords, - body, - decorator_list, - } => format_class_def(f, name, bases, keywords, body, decorator_list), - StmtKind::Return { value } => format_return(f, self.item, value.as_ref()), - StmtKind::Delete { targets } => format_delete(f, self.item, targets), - StmtKind::Assign { targets, value, .. } => format_assign(f, self.item, targets, value), - StmtKind::AugAssign { target, op, value } => { - format_aug_assign(f, self.item, target, op, value) - } - StmtKind::AnnAssign { - target, - annotation, - value, - simple, - } => format_ann_assign(f, self.item, target, annotation, value.as_deref(), *simple), - StmtKind::For { - target, - iter, - body, - orelse, - type_comment, - } => format_for( - f, - self.item, - target, - iter, - body, - orelse.as_ref(), - type_comment.as_deref(), - false, - ), - StmtKind::AsyncFor { - target, - iter, - body, - orelse, - type_comment, - } => format_for( - f, - self.item, - target, - iter, - body, - orelse.as_ref(), - type_comment.as_deref(), - true, - ), - StmtKind::While { test, body, orelse } => { - format_while(f, self.item, test, body, orelse.as_ref()) - } - StmtKind::If { - test, - body, - orelse, - is_elif, - } => format_if(f, test, body, orelse.as_ref(), *is_elif), - StmtKind::With { - items, - body, - type_comment, - } => format_with_( - f, - self.item, - items, - body, - type_comment.as_ref().map(String::as_str), - false, - ), - StmtKind::AsyncWith { - items, - body, - type_comment, - } => format_with_( - f, - self.item, - items, - body, - type_comment.as_ref().map(String::as_str), - true, - ), - StmtKind::Match { subject, cases } => format_match(f, self.item, subject, cases), - StmtKind::Raise { exc, cause } => { - format_raise(f, self.item, exc.as_deref(), cause.as_deref()) - } - StmtKind::Try { - body, - handlers, - orelse, - finalbody, - } => format_try( - f, - self.item, - body, - handlers, - orelse.as_ref(), - finalbody.as_ref(), - ), - StmtKind::TryStar { - body, - handlers, - orelse, - finalbody, - } => format_try_star( - f, - self.item, - body, - handlers, - orelse.as_ref(), - finalbody.as_ref(), - ), - StmtKind::Assert { test, msg } => { - format_assert(f, self.item, test, msg.as_ref().map(|expr| &**expr)) - } - StmtKind::Import { names } => format_import(f, self.item, names), - StmtKind::ImportFrom { - module, - names, - level, - } => format_import_from( - f, - self.item, - module.as_ref().map(String::as_str), - names, - *level, - ), - StmtKind::Expr { value } => format_expr(f, self.item, value), - }?; - - write!(f, [hard_line_break()])?; - write!(f, [trailing_comments(self.item)])?; - - Ok(()) - } -} - -impl AsFormat> for Stmt { - type Format<'a> = FormatStmt<'a>; - - fn format(&self) -> Self::Format<'_> { - FormatStmt { item: self } - } -} diff --git a/crates/ruff_python_formatter/src/format/strings.rs b/crates/ruff_python_formatter/src/format/strings.rs deleted file mode 100644 index 2046608295807..0000000000000 --- a/crates/ruff_python_formatter/src/format/strings.rs +++ /dev/null @@ -1,244 +0,0 @@ -use rustpython_parser::{Mode, Tok}; - -use crate::prelude::*; -use ruff_formatter::{write, Format}; -use ruff_python_ast::str::{leading_quote, trailing_quote}; -use ruff_text_size::TextRange; - -use crate::cst::Expr; - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub(crate) struct StringLiteralPart { - range: TextRange, -} - -impl Format> for StringLiteralPart { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let contents = f.context().contents(); - - // Extract leading and trailing quotes. - let contents = &contents[self.range]; - let leading_quote = leading_quote(contents).unwrap(); - let trailing_quote = trailing_quote(contents).unwrap(); - let body = &contents[leading_quote.len()..contents.len() - trailing_quote.len()]; - - // Determine the correct quote style. - // TODO(charlie): Make this parameterizable. - let mut squotes: usize = 0; - let mut dquotes: usize = 0; - for char in body.chars() { - if char == '\'' { - squotes += 1; - } else if char == '"' { - dquotes += 1; - } - } - - // Retain raw prefixes. - let mut is_raw = false; - if leading_quote.contains('r') { - is_raw = true; - f.write_element(FormatElement::StaticText { text: "r" })?; - } else if leading_quote.contains('R') { - is_raw = true; - f.write_element(FormatElement::StaticText { text: "R" })?; - } - - // Normalize bytes literals to use b"...". - if leading_quote.contains('b') || leading_quote.contains('B') { - f.write_element(FormatElement::StaticText { text: "b" })?; - } - - // TODO(charlie): Avoid allocating if there's nothing to escape. In other words, if we can - // use the string body directly, do so! - if trailing_quote.len() == 1 { - // Single-quoted string. - if dquotes == 0 || squotes > 0 { - // If the body doesn't contain any double quotes, or it contains both single and - // double quotes, use double quotes. - f.write_element(FormatElement::StaticText { text: "\"" })?; - f.write_element(FormatElement::DynamicText { - text: if is_raw { - body.into() - } else { - double_escape(body).into() - }, - })?; - f.write_element(FormatElement::StaticText { text: "\"" })?; - Ok(()) - } else { - f.write_element(FormatElement::StaticText { text: "'" })?; - f.write_element(FormatElement::DynamicText { - text: if is_raw { - body.into() - } else { - single_escape(body).into() - }, - })?; - f.write_element(FormatElement::StaticText { text: "'" })?; - Ok(()) - } - } else if trailing_quote.len() == 3 { - // Triple-quoted string. - if body.starts_with("\"\"\"") || body.ends_with('"') { - // We only need to use single quotes if the string body starts with three or more - // double quotes, or ends with a double quote. Converting to double quotes in those - // cases would cause a syntax error. - f.write_element(FormatElement::StaticText { text: "'''" })?; - f.write_element(FormatElement::DynamicText { - text: body.to_string().into_boxed_str(), - })?; - f.write_element(FormatElement::StaticText { text: "'''" })?; - Ok(()) - } else { - f.write_element(FormatElement::StaticText { text: "\"\"\"" })?; - f.write_element(FormatElement::DynamicText { - text: body.to_string().into_boxed_str(), - })?; - f.write_element(FormatElement::StaticText { text: "\"\"\"" })?; - Ok(()) - } - } else { - unreachable!("Invalid quote length: {}", trailing_quote.len()); - } - } -} - -#[inline] -pub(crate) const fn string_literal_part(range: TextRange) -> StringLiteralPart { - StringLiteralPart { range } -} - -#[derive(Debug, Copy, Clone)] -pub(crate) struct StringLiteral<'a> { - expr: &'a Expr, -} - -impl Format> for StringLiteral<'_> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let expr = self.expr; - - // TODO(charlie): This tokenization needs to happen earlier, so that we can attach - // comments to individual string literals. - let contents = f.context().locator().slice(expr.range()); - let elts = rustpython_parser::lexer::lex_starts_at(contents, Mode::Module, expr.start()) - .flatten() - .filter_map(|(tok, range)| { - if matches!(tok, Tok::String { .. }) { - Some(range) - } else { - None - } - }) - .collect::>(); - write!( - f, - [group(&format_with(|f| { - if expr.parentheses.is_if_expanded() { - write!(f, [if_group_breaks(&text("("))])?; - } - for (i, elt) in elts.iter().enumerate() { - write!(f, [string_literal_part(*elt)])?; - if i < elts.len() - 1 { - write!(f, [soft_line_break_or_space()])?; - } - } - if expr.parentheses.is_if_expanded() { - write!(f, [if_group_breaks(&text(")"))])?; - } - Ok(()) - }))] - )?; - Ok(()) - } -} - -#[inline] -pub(crate) const fn string_literal(expr: &Expr) -> StringLiteral { - StringLiteral { expr } -} - -/// Escape a string body to be used in a string literal with double quotes. -fn double_escape(text: &str) -> String { - let mut escaped = String::with_capacity(text.len()); - let mut chars = text.chars().peekable(); - while let Some(ch) = chars.next() { - if ch == '\\' { - let Some(next) = chars.peek() else { - break; - }; - if *next == '\'' { - chars.next(); - escaped.push('\''); - } else if *next == '"' { - chars.next(); - escaped.push('\\'); - escaped.push('"'); - } else if *next == '\\' { - chars.next(); - escaped.push('\\'); - escaped.push(ch); - } else { - escaped.push(ch); - } - } else if ch == '"' { - escaped.push('\\'); - escaped.push('"'); - } else { - escaped.push(ch); - } - } - escaped -} - -/// Escape a string body to be used in a string literal with single quotes. -fn single_escape(text: &str) -> String { - let mut escaped = String::with_capacity(text.len()); - let mut chars = text.chars().peekable(); - while let Some(ch) = chars.next() { - if ch == '\\' { - let Some(next) = chars.peek() else { - break; - }; - if *next == '"' { - chars.next(); - escaped.push('"'); - } else if *next == '\'' { - chars.next(); - escaped.push('\\'); - escaped.push('\''); - } else if *next == '\\' { - chars.next(); - escaped.push('\\'); - escaped.push(ch); - } else { - escaped.push(ch); - } - } else if ch == '\'' { - escaped.push('\\'); - escaped.push('\''); - } else { - escaped.push(ch); - } - } - escaped -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_double_escape() { - assert_eq!(double_escape(r#"It\'s mine"#), r#"It's mine"#); - assert_eq!(double_escape(r#"It\'s "mine""#), r#"It's \"mine\""#); - assert_eq!(double_escape(r#"It\\'s mine"#), r#"It\\'s mine"#); - } - - #[test] - fn test_single_escape() { - assert_eq!(single_escape(r#"It's mine"#), r#"It\'s mine"#); - assert_eq!(single_escape(r#"It\'s "mine""#), r#"It\'s "mine""#); - assert_eq!(single_escape(r#"It\\'s mine"#), r#"It\\\'s mine"#); - } -} diff --git a/crates/ruff_python_formatter/src/format/unary_op.rs b/crates/ruff_python_formatter/src/format/unary_op.rs deleted file mode 100644 index b16220fee918e..0000000000000 --- a/crates/ruff_python_formatter/src/format/unary_op.rs +++ /dev/null @@ -1,35 +0,0 @@ -use crate::prelude::*; -use ruff_formatter::write; - -use crate::cst::{UnaryOp, UnaryOpKind}; - -pub(crate) struct FormatUnaryOp<'a> { - item: &'a UnaryOp, -} - -impl AsFormat> for UnaryOp { - type Format<'a> = FormatUnaryOp<'a>; - - fn format(&self) -> Self::Format<'_> { - FormatUnaryOp { item: self } - } -} - -impl Format> for FormatUnaryOp<'_> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let unary_op = self.item; - write!( - f, - [ - text(match unary_op.node { - UnaryOpKind::Invert => "~", - UnaryOpKind::Not => "not", - UnaryOpKind::UAdd => "+", - UnaryOpKind::USub => "-", - }), - matches!(unary_op.node, UnaryOpKind::Not).then_some(space()) - ] - )?; - Ok(()) - } -} diff --git a/crates/ruff_python_formatter/src/format/withitem.rs b/crates/ruff_python_formatter/src/format/withitem.rs deleted file mode 100644 index 6b48dcf6e0d3e..0000000000000 --- a/crates/ruff_python_formatter/src/format/withitem.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::prelude::*; -use ruff_formatter::write; - -use crate::cst::Withitem; - -pub(crate) struct FormatWithitem<'a> { - item: &'a Withitem, -} - -impl AsFormat> for Withitem { - type Format<'a> = FormatWithitem<'a>; - - fn format(&self) -> Self::Format<'_> { - FormatWithitem { item: self } - } -} - -impl Format> for FormatWithitem<'_> { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - let withitem = self.item; - - write!(f, [withitem.context_expr.format()])?; - if let Some(optional_vars) = &withitem.optional_vars { - write!(f, [space(), text("as"), space()])?; - write!(f, [optional_vars.format()])?; - } - - Ok(()) - } -} diff --git a/crates/ruff_python_formatter/src/lib.rs b/crates/ruff_python_formatter/src/lib.rs index 2a0b24d139672..a755b05b6f22b 100644 --- a/crates/ruff_python_formatter/src/lib.rs +++ b/crates/ruff_python_formatter/src/lib.rs @@ -8,24 +8,15 @@ use ruff_formatter::{ }; use ruff_python_ast::source_code::{CommentRanges, CommentRangesBuilder, Locator}; -use crate::attachment::attach; use crate::comments::Comments; use crate::context::ASTFormatContext; -use crate::cst::Stmt; -use crate::newlines::normalize_newlines; -use crate::parentheses::normalize_parentheses; -use crate::trivia::TriviaToken; +use crate::module::FormatModule; -mod attachment; pub mod cli; mod comments; pub mod context; -mod cst; -mod format; -mod newlines; -mod parentheses; +mod module; mod prelude; -mod trivia; include!("../../ruff_formatter/shared_traits.rs"); @@ -46,12 +37,10 @@ pub fn fmt(contents: &str) -> Result { let comment_ranges = comment_ranges.finish(); - let trivia = trivia::extract_trivia_tokens(&tokens); - // Parse the AST. let python_ast = parse_tokens(tokens, Mode::Module, "").unwrap(); - let formatted = format_node(&python_ast, &comment_ranges, contents, trivia)?; + let formatted = format_node(&python_ast, &comment_ranges, contents)?; formatted .print() @@ -62,27 +51,11 @@ pub(crate) fn format_node<'a>( root: &'a Mod, comment_ranges: &'a CommentRanges, source: &'a str, - trivia: Vec, ) -> FormatResult>> { let comments = Comments::from_ast(root, SourceCode::new(source), comment_ranges); - let module = root.as_module().unwrap(); - let locator = Locator::new(source); - // Convert to a CST. - let mut python_cst: Vec = module - .body - .iter() - .cloned() - .map(|stmt| (stmt, &locator).into()) - .collect(); - - // Attach trivia. - attach(&mut python_cst, trivia); - normalize_newlines(&mut python_cst); - normalize_parentheses(&mut python_cst, &locator); - format!( ASTFormatContext::new( SimpleFormatOptions { @@ -92,7 +65,7 @@ pub(crate) fn format_node<'a>( locator.contents(), comments ), - [format::builders::statements(&python_cst)] + [FormatModule::new(root)] ) } @@ -111,15 +84,9 @@ mod tests { use ruff_testing_macros::fixture; use similar::TextDiff; - use crate::{fmt, format_node, trivia}; + use crate::{fmt, format_node}; - #[fixture( - pattern = "resources/test/fixtures/black/**/*.py", - // Excluded tests because they reach unreachable when attaching tokens - exclude = [ - "*comments8.py", - ]) - ] + #[fixture(pattern = "resources/test/fixtures/black/**/*.py")] #[test] fn black_test(input_path: &Path) -> Result<()> { let content = fs::read_to_string(input_path)?; @@ -212,12 +179,10 @@ mod tests { let comment_ranges = comment_ranges.finish(); - let trivia = trivia::extract_trivia_tokens(&tokens); - // Parse the AST. let python_ast = parse_tokens(tokens, Mode::Module, "").unwrap(); - let formatted = format_node(&python_ast, &comment_ranges, src, trivia).unwrap(); + let formatted = format_node(&python_ast, &comment_ranges, src).unwrap(); // Uncomment the `dbg` to print the IR. // Use `dbg_write!(f, []) instead of `write!(f, [])` in your formatting code to print some IR diff --git a/crates/ruff_python_formatter/src/module/mod.rs b/crates/ruff_python_formatter/src/module/mod.rs new file mode 100644 index 0000000000000..694414d0fb6c7 --- /dev/null +++ b/crates/ruff_python_formatter/src/module/mod.rs @@ -0,0 +1,33 @@ +use crate::context::ASTFormatContext; +use ruff_formatter::format_element::tag::VerbatimKind; +use ruff_formatter::prelude::*; +use ruff_formatter::write; +use rustpython_parser::ast::{Mod, Ranged}; + +pub(crate) struct FormatModule<'a> { + module: &'a Mod, +} + +impl<'a> FormatModule<'a> { + pub(crate) fn new(module: &'a Mod) -> Self { + Self { module } + } +} + +impl Format> for FormatModule<'_> { + fn fmt(&self, f: &mut Formatter>) -> FormatResult<()> { + let range = self.module.range(); + + write!(f, [source_position(range.start())])?; + + f.write_element(FormatElement::Tag(Tag::StartVerbatim( + VerbatimKind::Verbatim { + length: range.len(), + }, + )))?; + write!(f, [source_text_slice(range, ContainsNewlines::Detect)])?; + f.write_element(FormatElement::Tag(Tag::EndVerbatim))?; + + write!(f, [source_position(range.end())]) + } +} diff --git a/crates/ruff_python_formatter/src/newlines.rs b/crates/ruff_python_formatter/src/newlines.rs deleted file mode 100644 index 01a731f61fb2b..0000000000000 --- a/crates/ruff_python_formatter/src/newlines.rs +++ /dev/null @@ -1,370 +0,0 @@ -use rustpython_parser::ast::Constant; - -use crate::cst::visitor; -use crate::cst::visitor::Visitor; -use crate::cst::{ - Alias, Arg, BoolOp, CmpOp, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Keyword, Operator, - Pattern, SliceIndex, Stmt, StmtKind, UnaryOp, -}; -use crate::trivia::{Relationship, Trivia, TriviaKind}; - -#[derive(Debug, Copy, Clone)] -enum Depth { - TopLevel, - Nested, -} - -impl Depth { - fn max_newlines(self) -> usize { - match self { - Self::TopLevel => 2, - Self::Nested => 1, - } - } -} - -#[derive(Debug, Copy, Clone)] -enum Scope { - Module, - Class, - Function, -} - -#[derive(Debug, Copy, Clone)] -enum Trailer { - None, - ClassDef, - FunctionDef, - Import, - Docstring, - Generic, - CompoundStatement, -} - -struct StmtNormalizer { - depth: Depth, - trailer: Trailer, - scope: Scope, -} - -impl<'a> Visitor<'a> for StmtNormalizer { - fn visit_stmt(&mut self, stmt: &'a mut Stmt) { - // Remove any runs of empty lines greater than two in a row. - let mut count = 0; - stmt.trivia.retain(|c| { - if c.kind.is_empty_line() && c.relationship.is_leading() { - count += 1; - count <= self.depth.max_newlines() - } else { - count = 0; - true - } - }); - - if matches!(self.trailer, Trailer::None) - || (matches!(self.trailer, Trailer::CompoundStatement) - && !matches!( - stmt.node, - StmtKind::FunctionDef { .. } - | StmtKind::AsyncFunctionDef { .. } - | StmtKind::ClassDef { .. } - )) - { - // If this is the first statement in the block, remove any leading empty lines, with the - // exception being functions and classes defined within compound statements (e.g., as - // the first statement in an `if` body). - let mut seen_non_empty = false; - stmt.trivia.retain(|c| { - if seen_non_empty { - true - } else { - if c.kind.is_empty_line() && c.relationship.is_leading() { - false - } else { - seen_non_empty = true; - true - } - } - }); - } else { - // If the previous statement was a function or similar, ensure we have the - // appropriate number of lines to start. - let required_newlines = match self.trailer { - Trailer::FunctionDef | Trailer::ClassDef => self.depth.max_newlines(), - Trailer::Docstring if matches!(self.scope, Scope::Class) => 1, - Trailer::Import => usize::from(!matches!( - stmt.node, - StmtKind::Import { .. } | StmtKind::ImportFrom { .. } - )), - _ => 0, - }; - let present_newlines = stmt - .trivia - .iter() - .take_while(|c| c.kind.is_empty_line() && c.relationship.is_leading()) - .count(); - if present_newlines < required_newlines { - for _ in 0..(required_newlines - present_newlines) { - stmt.trivia.insert( - 0, - Trivia { - kind: TriviaKind::EmptyLine, - relationship: Relationship::Leading, - }, - ); - } - } - - // If the current statement is a function or similar, Ensure we have an - // appropriate number of lines above. - if matches!( - stmt.node, - StmtKind::FunctionDef { .. } - | StmtKind::AsyncFunctionDef { .. } - | StmtKind::ClassDef { .. } - ) { - let num_to_insert = self.depth.max_newlines() - - stmt - .trivia - .iter() - .take_while(|c| c.kind.is_empty_line() && c.relationship.is_leading()) - .count(); - for _ in 0..num_to_insert { - stmt.trivia.insert( - 0, - Trivia { - kind: TriviaKind::EmptyLine, - relationship: Relationship::Leading, - }, - ); - } - } - } - - let prev_scope = self.scope; - let prev_depth = self.depth; - - match &mut stmt.node { - StmtKind::FunctionDef { body, .. } | StmtKind::AsyncFunctionDef { body, .. } => { - self.depth = Depth::Nested; - self.scope = Scope::Function; - self.trailer = Trailer::None; - self.visit_body(body); - self.trailer = Trailer::FunctionDef; - } - StmtKind::ClassDef { body, .. } => { - self.depth = Depth::Nested; - self.scope = Scope::Class; - self.trailer = Trailer::None; - self.visit_body(body); - self.trailer = Trailer::ClassDef; - } - StmtKind::While { body, orelse, .. } - | StmtKind::For { body, orelse, .. } - | StmtKind::AsyncFor { body, orelse, .. } => { - self.depth = Depth::Nested; - self.trailer = Trailer::CompoundStatement; - self.visit_body(body); - - if let Some(orelse) = orelse { - // If the previous body ended with a function or class definition, we need to - // insert an empty line before the else block. Since the `else` itself isn't - // a statement, we need to insert it into the last statement of the body. - if matches!(self.trailer, Trailer::ClassDef | Trailer::FunctionDef) { - body.trivia.push(Trivia { - kind: TriviaKind::EmptyLine, - relationship: Relationship::Dangling, - }); - } - - self.depth = Depth::Nested; - self.trailer = Trailer::CompoundStatement; - self.visit_body(orelse); - } - } - StmtKind::If { body, orelse, .. } => { - self.depth = Depth::Nested; - self.trailer = Trailer::CompoundStatement; - self.visit_body(body); - - if let Some(orelse) = orelse { - if matches!(self.trailer, Trailer::ClassDef | Trailer::FunctionDef) { - body.trivia.push(Trivia { - kind: TriviaKind::EmptyLine, - relationship: Relationship::Dangling, - }); - } - - self.depth = Depth::Nested; - self.trailer = Trailer::CompoundStatement; - self.visit_body(orelse); - } - } - StmtKind::With { body, .. } | StmtKind::AsyncWith { body, .. } => { - self.depth = Depth::Nested; - self.trailer = Trailer::CompoundStatement; - self.visit_body(body); - } - // StmtKind::Match { .. } => {} - StmtKind::Try { - body, - handlers, - orelse, - finalbody, - } - | StmtKind::TryStar { - body, - handlers, - orelse, - finalbody, - } => { - self.depth = Depth::Nested; - self.trailer = Trailer::CompoundStatement; - self.visit_body(body); - - let mut prev = &mut body.trivia; - - for handler in handlers { - if matches!(self.trailer, Trailer::ClassDef | Trailer::FunctionDef) { - prev.push(Trivia { - kind: TriviaKind::EmptyLine, - relationship: Relationship::Dangling, - }); - } - - self.depth = Depth::Nested; - self.trailer = Trailer::CompoundStatement; - let ExcepthandlerKind::ExceptHandler { body, .. } = &mut handler.node; - self.visit_body(body); - prev = &mut body.trivia; - } - - if let Some(orelse) = orelse { - if matches!(self.trailer, Trailer::ClassDef | Trailer::FunctionDef) { - prev.push(Trivia { - kind: TriviaKind::EmptyLine, - relationship: Relationship::Dangling, - }); - } - - self.depth = Depth::Nested; - self.trailer = Trailer::CompoundStatement; - self.visit_body(orelse); - prev = &mut body.trivia; - } - - if let Some(finalbody) = finalbody { - if matches!(self.trailer, Trailer::ClassDef | Trailer::FunctionDef) { - prev.push(Trivia { - kind: TriviaKind::EmptyLine, - relationship: Relationship::Dangling, - }); - } - - self.depth = Depth::Nested; - self.trailer = Trailer::CompoundStatement; - self.visit_body(finalbody); - } - } - _ => { - self.trailer = match &stmt.node { - StmtKind::Expr { value, .. } - if matches!(self.scope, Scope::Class | Scope::Function) - && matches!(self.trailer, Trailer::None) => - { - if let ExprKind::Constant { - value: Constant::Str(..), - .. - } = &value.node - { - Trailer::Docstring - } else { - Trailer::Generic - } - } - StmtKind::Import { .. } | StmtKind::ImportFrom { .. } => Trailer::Import, - _ => Trailer::Generic, - }; - visitor::walk_stmt(self, stmt); - } - } - - self.depth = prev_depth; - self.scope = prev_scope; - } -} - -struct ExprNormalizer; - -impl<'a> Visitor<'a> for ExprNormalizer { - fn visit_expr(&mut self, expr: &'a mut Expr) { - expr.trivia.retain(|c| !c.kind.is_empty_line()); - visitor::walk_expr(self, expr); - } - - fn visit_alias(&mut self, alias: &'a mut Alias) { - alias.trivia.retain(|c| !c.kind.is_empty_line()); - visitor::walk_alias(self, alias); - } - - fn visit_arg(&mut self, arg: &'a mut Arg) { - arg.trivia.retain(|c| !c.kind.is_empty_line()); - visitor::walk_arg(self, arg); - } - - fn visit_excepthandler(&mut self, excepthandler: &'a mut Excepthandler) { - excepthandler.trivia.retain(|c| !c.kind.is_empty_line()); - visitor::walk_excepthandler(self, excepthandler); - } - - fn visit_keyword(&mut self, keyword: &'a mut Keyword) { - keyword.trivia.retain(|c| !c.kind.is_empty_line()); - visitor::walk_keyword(self, keyword); - } - - fn visit_bool_op(&mut self, bool_op: &'a mut BoolOp) { - bool_op.trivia.retain(|c| !c.kind.is_empty_line()); - visitor::walk_bool_op(self, bool_op); - } - - fn visit_unary_op(&mut self, unary_op: &'a mut UnaryOp) { - unary_op.trivia.retain(|c| !c.kind.is_empty_line()); - visitor::walk_unary_op(self, unary_op); - } - - fn visit_cmp_op(&mut self, cmp_op: &'a mut CmpOp) { - cmp_op.trivia.retain(|c| !c.kind.is_empty_line()); - visitor::walk_cmp_op(self, cmp_op); - } - - fn visit_operator(&mut self, operator: &'a mut Operator) { - operator.trivia.retain(|c| !c.kind.is_empty_line()); - visitor::walk_operator(self, operator); - } - - fn visit_slice_index(&mut self, slice_index: &'a mut SliceIndex) { - slice_index.trivia.retain(|c| !c.kind.is_empty_line()); - visitor::walk_slice_index(self, slice_index); - } - - fn visit_pattern(&mut self, pattern: &'a mut Pattern) { - pattern.trivia.retain(|c| !c.kind.is_empty_line()); - visitor::walk_pattern(self, pattern); - } -} - -pub(crate) fn normalize_newlines(python_cst: &mut [Stmt]) { - let mut normalizer = StmtNormalizer { - depth: Depth::TopLevel, - trailer: Trailer::None, - scope: Scope::Module, - }; - for stmt in python_cst.iter_mut() { - normalizer.visit_stmt(stmt); - } - - let mut normalizer = ExprNormalizer; - for stmt in python_cst.iter_mut() { - normalizer.visit_stmt(stmt); - } -} diff --git a/crates/ruff_python_formatter/src/parentheses.rs b/crates/ruff_python_formatter/src/parentheses.rs deleted file mode 100644 index 998517e142e60..0000000000000 --- a/crates/ruff_python_formatter/src/parentheses.rs +++ /dev/null @@ -1,199 +0,0 @@ -use rustpython_parser::ast::Constant; - -use ruff_python_ast::source_code::Locator; - -use crate::cst::helpers::is_radix_literal; -use crate::cst::visitor; -use crate::cst::visitor::Visitor; -use crate::cst::{Expr, ExprKind, Stmt, StmtKind}; -use crate::trivia::Parenthesize; - -/// Modify an [`Expr`] to infer parentheses, rather than respecting any user-provided trivia. -fn use_inferred_parens(expr: &mut Expr) { - // Remove parentheses, unless it's a generator expression, in which case, keep them. - if !matches!(expr.node, ExprKind::GeneratorExp { .. }) { - expr.trivia.retain(|trivia| !trivia.kind.is_parentheses()); - } - - // If it's a tuple, add parentheses if it's a singleton; otherwise, we only need parentheses - // if the tuple expands. - if let ExprKind::Tuple { elts, .. } = &expr.node { - expr.parentheses = if elts.len() > 1 { - Parenthesize::IfExpanded - } else { - Parenthesize::Always - }; - } -} - -struct ParenthesesNormalizer<'a> { - locator: &'a Locator<'a>, -} - -impl<'a> Visitor<'a> for ParenthesesNormalizer<'_> { - fn visit_stmt(&mut self, stmt: &'a mut Stmt) { - // Always remove parentheses around statements, unless it's an expression statement, - // in which case, remove parentheses around the expression. - let before = stmt.trivia.len(); - stmt.trivia.retain(|trivia| !trivia.kind.is_parentheses()); - let after = stmt.trivia.len(); - if let StmtKind::Expr { value } = &mut stmt.node { - if before != after { - stmt.parentheses = Parenthesize::Always; - value.parentheses = Parenthesize::Never; - } - } - - // In a variety of contexts, remove parentheses around sub-expressions. Right now, the - // pattern is consistent (and repeated), but it may not end up that way. - // https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#parentheses - match &mut stmt.node { - StmtKind::FunctionDef { .. } => {} - StmtKind::AsyncFunctionDef { .. } => {} - StmtKind::ClassDef { .. } => {} - StmtKind::Return { value } => { - if let Some(value) = value { - use_inferred_parens(value); - } - } - StmtKind::Delete { .. } => {} - StmtKind::Assign { targets, value, .. } => { - for target in targets { - use_inferred_parens(target); - } - use_inferred_parens(value); - } - StmtKind::AugAssign { value, .. } => { - use_inferred_parens(value); - } - StmtKind::AnnAssign { value, .. } => { - if let Some(value) = value { - use_inferred_parens(value); - } - } - StmtKind::For { target, iter, .. } | StmtKind::AsyncFor { target, iter, .. } => { - use_inferred_parens(target); - if !matches!(iter.node, ExprKind::Tuple { .. }) { - use_inferred_parens(iter); - } - } - StmtKind::While { test, .. } => { - use_inferred_parens(test); - } - StmtKind::If { test, .. } => { - use_inferred_parens(test); - } - StmtKind::With { .. } => {} - StmtKind::AsyncWith { .. } => {} - StmtKind::Match { .. } => {} - StmtKind::Raise { .. } => {} - StmtKind::Try { .. } => {} - StmtKind::TryStar { .. } => {} - StmtKind::Assert { test, msg } => { - use_inferred_parens(test); - if let Some(msg) = msg { - use_inferred_parens(msg); - } - } - StmtKind::Import { .. } => {} - StmtKind::ImportFrom { .. } => {} - StmtKind::Global { .. } => {} - StmtKind::Nonlocal { .. } => {} - StmtKind::Expr { .. } => {} - StmtKind::Pass => {} - StmtKind::Break => {} - StmtKind::Continue => {} - } - - visitor::walk_stmt(self, stmt); - } - - fn visit_expr(&mut self, expr: &'a mut Expr) { - // Always retain parentheses around expressions. - let before = expr.trivia.len(); - expr.trivia.retain(|trivia| !trivia.kind.is_parentheses()); - let after = expr.trivia.len(); - if before != after { - expr.parentheses = Parenthesize::Always; - } - - match &mut expr.node { - ExprKind::BoolOp { .. } => {} - ExprKind::NamedExpr { .. } => {} - ExprKind::BinOp { .. } => {} - ExprKind::UnaryOp { .. } => {} - ExprKind::Lambda { .. } => {} - ExprKind::IfExp { .. } => {} - ExprKind::Dict { .. } => {} - ExprKind::Set { .. } => {} - ExprKind::ListComp { .. } => {} - ExprKind::SetComp { .. } => {} - ExprKind::DictComp { .. } => {} - ExprKind::GeneratorExp { .. } => {} - ExprKind::Await { .. } => {} - ExprKind::Yield { .. } => {} - ExprKind::YieldFrom { .. } => {} - ExprKind::Compare { .. } => {} - ExprKind::Call { .. } => {} - ExprKind::FormattedValue { .. } => {} - ExprKind::JoinedStr { .. } => {} - ExprKind::Constant { .. } => {} - ExprKind::Attribute { value, .. } => { - if matches!( - value.node, - ExprKind::Constant { - value: Constant::Float(..), - .. - }, - ) { - value.parentheses = Parenthesize::Always; - } else if matches!( - value.node, - ExprKind::Constant { - value: Constant::Int(..), - .. - }, - ) { - // TODO(charlie): Encode this in the AST via separate node types. - if !is_radix_literal(self.locator.slice(value.range())) { - value.parentheses = Parenthesize::Always; - } - } - } - ExprKind::Subscript { value, slice, .. } => { - // If the slice isn't manually parenthesized, ensure that we _never_ parenthesize - // the value. - if !slice - .trivia - .iter() - .any(|trivia| trivia.kind.is_parentheses()) - { - value.parentheses = Parenthesize::Never; - } - } - ExprKind::Starred { .. } => {} - ExprKind::Name { .. } => {} - ExprKind::List { .. } => {} - ExprKind::Tuple { .. } => {} - ExprKind::Slice { .. } => {} - } - - visitor::walk_expr(self, expr); - } -} - -/// Normalize parentheses in a Python CST. -/// -/// It's not always possible to determine the correct parentheses to use during formatting -/// from the node (and trivia) alone; sometimes, we need to know the parent node. This -/// visitor normalizes parentheses via a top-down traversal, which simplifies the formatting -/// code later on. -/// -/// TODO(charlie): It's weird that we have both `TriviaKind::Parentheses` (which aren't used -/// during formatting) and `Parenthesize` (which are used during formatting). -pub(crate) fn normalize_parentheses(python_cst: &mut [Stmt], locator: &Locator) { - let mut normalizer = ParenthesesNormalizer { locator }; - for stmt in python_cst { - normalizer.visit_stmt(stmt); - } -} diff --git a/crates/ruff_python_formatter/src/prelude.rs b/crates/ruff_python_formatter/src/prelude.rs index 01089927b27ca..b85bb97b2179c 100644 --- a/crates/ruff_python_formatter/src/prelude.rs +++ b/crates/ruff_python_formatter/src/prelude.rs @@ -1,3 +1,4 @@ #[allow(unused_imports)] pub(crate) use crate::{ASTFormatContext, AsFormat, FormattedIterExt as _, IntoFormat}; +#[allow(unused_imports)] pub(crate) use ruff_formatter::prelude::*; 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 new file mode 100644 index 0000000000000..0128d6ed458e3 --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__attribute_access_on_number_literals_py.snap @@ -0,0 +1,132 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/attribute_access_on_number_literals.py +--- +## Input + +```py +x = 123456789 .bit_count() +x = (123456).__abs__() +x = .1.is_integer() +x = 1. .imag +x = 1E+1.imag +x = 1E-1.real +x = 123456789.123456789.hex() +x = 123456789.123456789E123456789 .real +x = 123456789E123456789 .conjugate() +x = 123456789J.real +x = 123456789.123456789J.__add__(0b1011.bit_length()) +x = 0XB1ACC.conjugate() +x = 0B1011 .conjugate() +x = 0O777 .real +x = 0.000000006 .hex() +x = -100.0000J + +if 10 .real: + ... + +y = 100[no] +y = 100(no) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,21 +1,21 @@ +-x = (123456789).bit_count() ++x = 123456789 .bit_count() + x = (123456).__abs__() +-x = (0.1).is_integer() +-x = (1.0).imag +-x = (1e1).imag +-x = (1e-1).real +-x = (123456789.123456789).hex() +-x = (123456789.123456789e123456789).real +-x = (123456789e123456789).conjugate() +-x = 123456789j.real +-x = 123456789.123456789j.__add__(0b1011.bit_length()) +-x = 0xB1ACC.conjugate() +-x = 0b1011.conjugate() +-x = 0o777.real +-x = (0.000000006).hex() +-x = -100.0000j ++x = .1.is_integer() ++x = 1. .imag ++x = 1E+1.imag ++x = 1E-1.real ++x = 123456789.123456789.hex() ++x = 123456789.123456789E123456789 .real ++x = 123456789E123456789 .conjugate() ++x = 123456789J.real ++x = 123456789.123456789J.__add__(0b1011.bit_length()) ++x = 0XB1ACC.conjugate() ++x = 0B1011 .conjugate() ++x = 0O777 .real ++x = 0.000000006 .hex() ++x = -100.0000J + +-if (10).real: ++if 10 .real: + ... + + y = 100[no] +``` + +## Ruff Output + +```py +x = 123456789 .bit_count() +x = (123456).__abs__() +x = .1.is_integer() +x = 1. .imag +x = 1E+1.imag +x = 1E-1.real +x = 123456789.123456789.hex() +x = 123456789.123456789E123456789 .real +x = 123456789E123456789 .conjugate() +x = 123456789J.real +x = 123456789.123456789J.__add__(0b1011.bit_length()) +x = 0XB1ACC.conjugate() +x = 0B1011 .conjugate() +x = 0O777 .real +x = 0.000000006 .hex() +x = -100.0000J + +if 10 .real: + ... + +y = 100[no] +y = 100(no) +``` + +## Black Output + +```py +x = (123456789).bit_count() +x = (123456).__abs__() +x = (0.1).is_integer() +x = (1.0).imag +x = (1e1).imag +x = (1e-1).real +x = (123456789.123456789).hex() +x = (123456789.123456789e123456789).real +x = (123456789e123456789).conjugate() +x = 123456789j.real +x = 123456789.123456789j.__add__(0b1011.bit_length()) +x = 0xB1ACC.conjugate() +x = 0b1011.conjugate() +x = 0o777.real +x = (0.000000006).hex() +x = -100.0000j + +if (10).real: + ... + +y = 100[no] +y = 100(no) +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__beginning_backslash_py.snap similarity index 54% rename from crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip_py.snap rename to crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__beginning_backslash_py.snap index c4deeaed9cd65..df93318969b21 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__beginning_backslash_py.snap @@ -1,14 +1,18 @@ --- source: crates/ruff_python_formatter/src/lib.rs expression: snapshot -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/beginning_backslash.py --- ## Input ```py -a, b = 1, 2 -c = 6 # fmt: skip -d = 5 +\ + + + + + +print("hello, world") ``` ## Black Differences @@ -16,27 +20,32 @@ d = 5 ```diff --- Black +++ Ruff -@@ -1,3 +1,3 @@ - a, b = 1, 2 --c = 6 # fmt: skip -+c = 6 # fmt: skip - d = 5 +@@ -1 +1,7 @@ ++\ ++ ++ ++ ++ ++ + print("hello, world") ``` ## Ruff Output ```py -a, b = 1, 2 -c = 6 # fmt: skip -d = 5 +\ + + + + + +print("hello, world") ``` ## Black Output ```py -a, b = 1, 2 -c = 6 # fmt: skip -d = 5 +print("hello, world") ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__bracketmatch_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__bracketmatch_py.snap new file mode 100644 index 0000000000000..15d5a14567bd9 --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__bracketmatch_py.snap @@ -0,0 +1,54 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/bracketmatch.py +--- +## Input + +```py +for ((x in {}) or {})['a'] in x: + pass +pem_spam = lambda l, spam = { + "x": 3 +}: not spam.get(l.strip()) +lambda x=lambda y={1: 3}: y['x':lambda y: {1: 2}]: x +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,4 +1,6 @@ +-for ((x in {}) or {})["a"] in x: ++for ((x in {}) or {})['a'] in x: + pass +-pem_spam = lambda l, spam={"x": 3}: not spam.get(l.strip()) +-lambda x=lambda y={1: 3}: y["x" : lambda y: {1: 2}]: x ++pem_spam = lambda l, spam = { ++ "x": 3 ++}: not spam.get(l.strip()) ++lambda x=lambda y={1: 3}: y['x':lambda y: {1: 2}]: x +``` + +## Ruff Output + +```py +for ((x in {}) or {})['a'] in x: + pass +pem_spam = lambda l, spam = { + "x": 3 +}: not spam.get(l.strip()) +lambda x=lambda y={1: 3}: y['x':lambda y: {1: 2}]: x +``` + +## Black Output + +```py +for ((x in {}) or {})["a"] in x: + pass +pem_spam = lambda l, spam={"x": 3}: not spam.get(l.strip()) +lambda x=lambda y={1: 3}: y["x" : lambda y: {1: 2}]: x +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__class_blank_parentheses_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__class_blank_parentheses_py.snap new file mode 100644 index 0000000000000..12cec64049e8d --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__class_blank_parentheses_py.snap @@ -0,0 +1,141 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/class_blank_parentheses.py +--- +## Input + +```py +class SimpleClassWithBlankParentheses(): + pass +class ClassWithSpaceParentheses ( ): + first_test_data = 90 + second_test_data = 100 + def test_func(self): + return None +class ClassWithEmptyFunc(object): + + def func_with_blank_parentheses(): + return 5 + + +def public_func_with_blank_parentheses(): + return None +def class_under_the_func_with_blank_parentheses(): + class InsideFunc(): + pass +class NormalClass ( +): + def func_for_testing(self, first, second): + sum = first + second + return sum +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,30 +1,23 @@ +-class SimpleClassWithBlankParentheses: ++class SimpleClassWithBlankParentheses(): + pass +- +- +-class ClassWithSpaceParentheses: ++class ClassWithSpaceParentheses ( ): + first_test_data = 90 + second_test_data = 100 +- + def test_func(self): + return None +- ++class ClassWithEmptyFunc(object): + +-class ClassWithEmptyFunc(object): + def func_with_blank_parentheses(): + return 5 + + + def public_func_with_blank_parentheses(): + return None +- +- + def class_under_the_func_with_blank_parentheses(): +- class InsideFunc: ++ class InsideFunc(): + pass +- +- +-class NormalClass: ++class NormalClass ( ++): + def func_for_testing(self, first, second): + sum = first + second + return sum +``` + +## Ruff Output + +```py +class SimpleClassWithBlankParentheses(): + pass +class ClassWithSpaceParentheses ( ): + first_test_data = 90 + second_test_data = 100 + def test_func(self): + return None +class ClassWithEmptyFunc(object): + + def func_with_blank_parentheses(): + return 5 + + +def public_func_with_blank_parentheses(): + return None +def class_under_the_func_with_blank_parentheses(): + class InsideFunc(): + pass +class NormalClass ( +): + def func_for_testing(self, first, second): + sum = first + second + return sum +``` + +## Black Output + +```py +class SimpleClassWithBlankParentheses: + pass + + +class ClassWithSpaceParentheses: + first_test_data = 90 + second_test_data = 100 + + def test_func(self): + return None + + +class ClassWithEmptyFunc(object): + def func_with_blank_parentheses(): + return 5 + + +def public_func_with_blank_parentheses(): + return None + + +def class_under_the_func_with_blank_parentheses(): + class InsideFunc: + pass + + +class NormalClass: + def func_for_testing(self, first, second): + sum = first + second + return sum +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__class_methods_new_line_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__class_methods_new_line_py.snap new file mode 100644 index 0000000000000..5cdc7dd0c2fea --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__class_methods_new_line_py.snap @@ -0,0 +1,559 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/class_methods_new_line.py +--- +## Input + +```py +class ClassSimplest: + pass +class ClassWithSingleField: + a = 1 +class ClassWithJustTheDocstring: + """Just a docstring.""" +class ClassWithInit: + def __init__(self): + pass +class ClassWithTheDocstringAndInit: + """Just a docstring.""" + def __init__(self): + pass +class ClassWithInitAndVars: + cls_var = 100 + def __init__(self): + pass +class ClassWithInitAndVarsAndDocstring: + """Test class""" + cls_var = 100 + def __init__(self): + pass +class ClassWithDecoInit: + @deco + def __init__(self): + pass +class ClassWithDecoInitAndVars: + cls_var = 100 + @deco + def __init__(self): + pass +class ClassWithDecoInitAndVarsAndDocstring: + """Test class""" + cls_var = 100 + @deco + def __init__(self): + pass +class ClassSimplestWithInner: + class Inner: + pass +class ClassSimplestWithInnerWithDocstring: + class Inner: + """Just a docstring.""" + def __init__(self): + pass +class ClassWithSingleFieldWithInner: + a = 1 + class Inner: + pass +class ClassWithJustTheDocstringWithInner: + """Just a docstring.""" + class Inner: + pass +class ClassWithInitWithInner: + class Inner: + pass + def __init__(self): + pass +class ClassWithInitAndVarsWithInner: + cls_var = 100 + class Inner: + pass + def __init__(self): + pass +class ClassWithInitAndVarsAndDocstringWithInner: + """Test class""" + cls_var = 100 + class Inner: + pass + def __init__(self): + pass +class ClassWithDecoInitWithInner: + class Inner: + pass + @deco + def __init__(self): + pass +class ClassWithDecoInitAndVarsWithInner: + cls_var = 100 + class Inner: + pass + @deco + def __init__(self): + pass +class ClassWithDecoInitAndVarsAndDocstringWithInner: + """Test class""" + cls_var = 100 + class Inner: + pass + @deco + def __init__(self): + pass +class ClassWithDecoInitAndVarsAndDocstringWithInner2: + """Test class""" + class Inner: + pass + cls_var = 100 + @deco + def __init__(self): + pass +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,165 +1,100 @@ + class ClassSimplest: + pass +- +- + class ClassWithSingleField: + a = 1 +- +- + class ClassWithJustTheDocstring: + """Just a docstring.""" +- +- + class ClassWithInit: + def __init__(self): + pass +- +- + class ClassWithTheDocstringAndInit: + """Just a docstring.""" +- + def __init__(self): + pass +- +- + class ClassWithInitAndVars: + cls_var = 100 +- + def __init__(self): + pass +- +- + class ClassWithInitAndVarsAndDocstring: + """Test class""" +- + cls_var = 100 +- + def __init__(self): + pass +- +- + class ClassWithDecoInit: + @deco + def __init__(self): + pass +- +- + class ClassWithDecoInitAndVars: + cls_var = 100 +- + @deco + def __init__(self): + pass +- +- + class ClassWithDecoInitAndVarsAndDocstring: + """Test class""" +- + cls_var = 100 +- + @deco + def __init__(self): + pass +- +- + class ClassSimplestWithInner: + class Inner: + pass +- +- + class ClassSimplestWithInnerWithDocstring: + class Inner: + """Just a docstring.""" +- + def __init__(self): + pass +- +- + class ClassWithSingleFieldWithInner: + a = 1 +- + class Inner: + pass +- +- + class ClassWithJustTheDocstringWithInner: + """Just a docstring.""" +- + class Inner: + pass +- +- + class ClassWithInitWithInner: + class Inner: + pass +- + def __init__(self): + pass +- +- + class ClassWithInitAndVarsWithInner: + cls_var = 100 +- + class Inner: + pass +- + def __init__(self): + pass +- +- + class ClassWithInitAndVarsAndDocstringWithInner: + """Test class""" +- + cls_var = 100 +- + class Inner: + pass +- + def __init__(self): + pass +- +- + class ClassWithDecoInitWithInner: + class Inner: + pass +- + @deco + def __init__(self): + pass +- +- + class ClassWithDecoInitAndVarsWithInner: + cls_var = 100 +- + class Inner: + pass +- + @deco + def __init__(self): + pass +- +- + class ClassWithDecoInitAndVarsAndDocstringWithInner: + """Test class""" +- + cls_var = 100 +- + class Inner: + pass +- + @deco + def __init__(self): + pass +- +- + class ClassWithDecoInitAndVarsAndDocstringWithInner2: + """Test class""" +- + class Inner: + pass +- + cls_var = 100 +- + @deco + def __init__(self): + pass +``` + +## Ruff Output + +```py +class ClassSimplest: + pass +class ClassWithSingleField: + a = 1 +class ClassWithJustTheDocstring: + """Just a docstring.""" +class ClassWithInit: + def __init__(self): + pass +class ClassWithTheDocstringAndInit: + """Just a docstring.""" + def __init__(self): + pass +class ClassWithInitAndVars: + cls_var = 100 + def __init__(self): + pass +class ClassWithInitAndVarsAndDocstring: + """Test class""" + cls_var = 100 + def __init__(self): + pass +class ClassWithDecoInit: + @deco + def __init__(self): + pass +class ClassWithDecoInitAndVars: + cls_var = 100 + @deco + def __init__(self): + pass +class ClassWithDecoInitAndVarsAndDocstring: + """Test class""" + cls_var = 100 + @deco + def __init__(self): + pass +class ClassSimplestWithInner: + class Inner: + pass +class ClassSimplestWithInnerWithDocstring: + class Inner: + """Just a docstring.""" + def __init__(self): + pass +class ClassWithSingleFieldWithInner: + a = 1 + class Inner: + pass +class ClassWithJustTheDocstringWithInner: + """Just a docstring.""" + class Inner: + pass +class ClassWithInitWithInner: + class Inner: + pass + def __init__(self): + pass +class ClassWithInitAndVarsWithInner: + cls_var = 100 + class Inner: + pass + def __init__(self): + pass +class ClassWithInitAndVarsAndDocstringWithInner: + """Test class""" + cls_var = 100 + class Inner: + pass + def __init__(self): + pass +class ClassWithDecoInitWithInner: + class Inner: + pass + @deco + def __init__(self): + pass +class ClassWithDecoInitAndVarsWithInner: + cls_var = 100 + class Inner: + pass + @deco + def __init__(self): + pass +class ClassWithDecoInitAndVarsAndDocstringWithInner: + """Test class""" + cls_var = 100 + class Inner: + pass + @deco + def __init__(self): + pass +class ClassWithDecoInitAndVarsAndDocstringWithInner2: + """Test class""" + class Inner: + pass + cls_var = 100 + @deco + def __init__(self): + pass +``` + +## Black Output + +```py +class ClassSimplest: + pass + + +class ClassWithSingleField: + a = 1 + + +class ClassWithJustTheDocstring: + """Just a docstring.""" + + +class ClassWithInit: + def __init__(self): + pass + + +class ClassWithTheDocstringAndInit: + """Just a docstring.""" + + def __init__(self): + pass + + +class ClassWithInitAndVars: + cls_var = 100 + + def __init__(self): + pass + + +class ClassWithInitAndVarsAndDocstring: + """Test class""" + + cls_var = 100 + + def __init__(self): + pass + + +class ClassWithDecoInit: + @deco + def __init__(self): + pass + + +class ClassWithDecoInitAndVars: + cls_var = 100 + + @deco + def __init__(self): + pass + + +class ClassWithDecoInitAndVarsAndDocstring: + """Test class""" + + cls_var = 100 + + @deco + def __init__(self): + pass + + +class ClassSimplestWithInner: + class Inner: + pass + + +class ClassSimplestWithInnerWithDocstring: + class Inner: + """Just a docstring.""" + + def __init__(self): + pass + + +class ClassWithSingleFieldWithInner: + a = 1 + + class Inner: + pass + + +class ClassWithJustTheDocstringWithInner: + """Just a docstring.""" + + class Inner: + pass + + +class ClassWithInitWithInner: + class Inner: + pass + + def __init__(self): + pass + + +class ClassWithInitAndVarsWithInner: + cls_var = 100 + + class Inner: + pass + + def __init__(self): + pass + + +class ClassWithInitAndVarsAndDocstringWithInner: + """Test class""" + + cls_var = 100 + + class Inner: + pass + + def __init__(self): + pass + + +class ClassWithDecoInitWithInner: + class Inner: + pass + + @deco + def __init__(self): + pass + + +class ClassWithDecoInitAndVarsWithInner: + cls_var = 100 + + class Inner: + pass + + @deco + def __init__(self): + pass + + +class ClassWithDecoInitAndVarsAndDocstringWithInner: + """Test class""" + + cls_var = 100 + + class Inner: + pass + + @deco + def __init__(self): + pass + + +class ClassWithDecoInitAndVarsAndDocstringWithInner2: + """Test class""" + + class Inner: + pass + + cls_var = 100 + + @deco + def __init__(self): + pass +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__collections_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__collections_py.snap new file mode 100644 index 0000000000000..47db08c75f31f --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__collections_py.snap @@ -0,0 +1,370 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/collections.py +--- +## Input + +```py +import core, time, a + +from . import A, B, C + +# keeps existing trailing comma +from foo import ( + bar, +) + +# also keeps existing structure +from foo import ( + baz, + qux, +) + +# `as` works as well +from foo import ( + xyzzy as magic, +) + +a = {1,2,3,} +b = { +1,2, + 3} +c = { + 1, + 2, + 3, +} +x = 1, +y = narf(), +nested = {(1,2,3),(4,5,6),} +nested_no_trailing_comma = {(1,2,3),(4,5,6)} +nested_long_lines = ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "cccccccccccccccccccccccccccccccccccccccc", (1, 2, 3), "dddddddddddddddddddddddddddddddddddddddd"] +{"oneple": (1,),} +{"oneple": (1,)} +['ls', 'lsoneple/%s' % (foo,)] +x = {"oneple": (1,)} +y = {"oneple": (1,),} +assert False, ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa wraps %s" % bar) + +# looping over a 1-tuple should also not get wrapped +for x in (1,): + pass +for (x,) in (1,), (2,), (3,): + pass + +[1, 2, 3,] + +division_result_tuple = (6/2,) +print("foo %r", (foo.bar,)) + +if True: + IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING = ( + Config.IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING + | {pylons.controllers.WSGIController} + ) + +if True: + ec2client.get_waiter('instance_stopped').wait( + InstanceIds=[instance.id], + WaiterConfig={ + 'Delay': 5, + }) + ec2client.get_waiter("instance_stopped").wait( + InstanceIds=[instance.id], + WaiterConfig={"Delay": 5,}, + ) + ec2client.get_waiter("instance_stopped").wait( + InstanceIds=[instance.id], WaiterConfig={"Delay": 5,}, + ) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -18,44 +18,26 @@ + xyzzy as magic, + ) + +-a = { +- 1, +- 2, +- 3, +-} +-b = {1, 2, 3} ++a = {1,2,3,} ++b = { ++1,2, ++ 3} + c = { + 1, + 2, + 3, + } +-x = (1,) +-y = (narf(),) +-nested = { +- (1, 2, 3), +- (4, 5, 6), +-} +-nested_no_trailing_comma = {(1, 2, 3), (4, 5, 6)} +-nested_long_lines = [ +- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", +- "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", +- "cccccccccccccccccccccccccccccccccccccccc", +- (1, 2, 3), +- "dddddddddddddddddddddddddddddddddddddddd", +-] +-{ +- "oneple": (1,), +-} ++x = 1, ++y = narf(), ++nested = {(1,2,3),(4,5,6),} ++nested_no_trailing_comma = {(1,2,3),(4,5,6)} ++nested_long_lines = ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "cccccccccccccccccccccccccccccccccccccccc", (1, 2, 3), "dddddddddddddddddddddddddddddddddddddddd"] ++{"oneple": (1,),} + {"oneple": (1,)} +-["ls", "lsoneple/%s" % (foo,)] ++['ls', 'lsoneple/%s' % (foo,)] + x = {"oneple": (1,)} +-y = { +- "oneple": (1,), +-} +-assert False, ( +- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa wraps %s" +- % bar +-) ++y = {"oneple": (1,),} ++assert False, ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa wraps %s" % bar) + + # looping over a 1-tuple should also not get wrapped + for x in (1,): +@@ -63,13 +45,9 @@ + for (x,) in (1,), (2,), (3,): + pass + +-[ +- 1, +- 2, +- 3, +-] ++[1, 2, 3,] + +-division_result_tuple = (6 / 2,) ++division_result_tuple = (6/2,) + print("foo %r", (foo.bar,)) + + if True: +@@ -79,21 +57,15 @@ + ) + + if True: +- ec2client.get_waiter("instance_stopped").wait( ++ ec2client.get_waiter('instance_stopped').wait( + InstanceIds=[instance.id], + WaiterConfig={ +- "Delay": 5, +- }, +- ) ++ 'Delay': 5, ++ }) + ec2client.get_waiter("instance_stopped").wait( + InstanceIds=[instance.id], +- WaiterConfig={ +- "Delay": 5, +- }, ++ WaiterConfig={"Delay": 5,}, + ) + ec2client.get_waiter("instance_stopped").wait( +- InstanceIds=[instance.id], +- WaiterConfig={ +- "Delay": 5, +- }, ++ InstanceIds=[instance.id], WaiterConfig={"Delay": 5,}, + ) +``` + +## Ruff Output + +```py +import core, time, a + +from . import A, B, C + +# keeps existing trailing comma +from foo import ( + bar, +) + +# also keeps existing structure +from foo import ( + baz, + qux, +) + +# `as` works as well +from foo import ( + xyzzy as magic, +) + +a = {1,2,3,} +b = { +1,2, + 3} +c = { + 1, + 2, + 3, +} +x = 1, +y = narf(), +nested = {(1,2,3),(4,5,6),} +nested_no_trailing_comma = {(1,2,3),(4,5,6)} +nested_long_lines = ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "cccccccccccccccccccccccccccccccccccccccc", (1, 2, 3), "dddddddddddddddddddddddddddddddddddddddd"] +{"oneple": (1,),} +{"oneple": (1,)} +['ls', 'lsoneple/%s' % (foo,)] +x = {"oneple": (1,)} +y = {"oneple": (1,),} +assert False, ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa wraps %s" % bar) + +# looping over a 1-tuple should also not get wrapped +for x in (1,): + pass +for (x,) in (1,), (2,), (3,): + pass + +[1, 2, 3,] + +division_result_tuple = (6/2,) +print("foo %r", (foo.bar,)) + +if True: + IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING = ( + Config.IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING + | {pylons.controllers.WSGIController} + ) + +if True: + ec2client.get_waiter('instance_stopped').wait( + InstanceIds=[instance.id], + WaiterConfig={ + 'Delay': 5, + }) + ec2client.get_waiter("instance_stopped").wait( + InstanceIds=[instance.id], + WaiterConfig={"Delay": 5,}, + ) + ec2client.get_waiter("instance_stopped").wait( + InstanceIds=[instance.id], WaiterConfig={"Delay": 5,}, + ) +``` + +## Black Output + +```py +import core, time, a + +from . import A, B, C + +# keeps existing trailing comma +from foo import ( + bar, +) + +# also keeps existing structure +from foo import ( + baz, + qux, +) + +# `as` works as well +from foo import ( + xyzzy as magic, +) + +a = { + 1, + 2, + 3, +} +b = {1, 2, 3} +c = { + 1, + 2, + 3, +} +x = (1,) +y = (narf(),) +nested = { + (1, 2, 3), + (4, 5, 6), +} +nested_no_trailing_comma = {(1, 2, 3), (4, 5, 6)} +nested_long_lines = [ + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "cccccccccccccccccccccccccccccccccccccccc", + (1, 2, 3), + "dddddddddddddddddddddddddddddddddddddddd", +] +{ + "oneple": (1,), +} +{"oneple": (1,)} +["ls", "lsoneple/%s" % (foo,)] +x = {"oneple": (1,)} +y = { + "oneple": (1,), +} +assert False, ( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa wraps %s" + % bar +) + +# looping over a 1-tuple should also not get wrapped +for x in (1,): + pass +for (x,) in (1,), (2,), (3,): + pass + +[ + 1, + 2, + 3, +] + +division_result_tuple = (6 / 2,) +print("foo %r", (foo.bar,)) + +if True: + IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING = ( + Config.IGNORED_TYPES_FOR_ATTRIBUTE_CHECKING + | {pylons.controllers.WSGIController} + ) + +if True: + ec2client.get_waiter("instance_stopped").wait( + InstanceIds=[instance.id], + WaiterConfig={ + "Delay": 5, + }, + ) + ec2client.get_waiter("instance_stopped").wait( + InstanceIds=[instance.id], + WaiterConfig={ + "Delay": 5, + }, + ) + ec2client.get_waiter("instance_stopped").wait( + InstanceIds=[instance.id], + WaiterConfig={ + "Delay": 5, + }, + ) +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comment_after_escaped_newline_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comment_after_escaped_newline_py.snap new file mode 100644 index 0000000000000..5633eef26a402 --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comment_after_escaped_newline_py.snap @@ -0,0 +1,64 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comment_after_escaped_newline.py +--- +## Input + +```py +def bob(): \ + # pylint: disable=W9016 + pass + + +def bobtwo(): \ + \ + # some comment here + pass +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,6 +1,9 @@ +-def bob(): # pylint: disable=W9016 ++def bob(): \ ++ # pylint: disable=W9016 + pass + + +-def bobtwo(): # some comment here ++def bobtwo(): \ ++ \ ++ # some comment here + pass +``` + +## Ruff Output + +```py +def bob(): \ + # pylint: disable=W9016 + pass + + +def bobtwo(): \ + \ + # some comment here + pass +``` + +## Black Output + +```py +def bob(): # pylint: disable=W9016 + pass + + +def bobtwo(): # some comment here + pass +``` + + 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 67f21723d4f5f..f46a4ca2a1d01 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 @@ -178,119 +178,262 @@ instruction()#comment with bad spacing ```diff --- Black +++ Ruff -@@ -72,7 +72,11 @@ +@@ -1,39 +1,40 @@ + from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( +- MyLovelyCompanyTeamProjectComponent, # NOT DRY ++ MyLovelyCompanyTeamProjectComponent # NOT DRY + ) + from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( +- MyLovelyCompanyTeamProjectComponent as component, # DRY ++ MyLovelyCompanyTeamProjectComponent as component # DRY + ) + + # Please keep __all__ alphabetized within each category. + + __all__ = [ + # Super-special typing primitives. +- "Any", +- "Callable", +- "ClassVar", ++ 'Any', ++ 'Callable', ++ 'ClassVar', ++ + # ABCs (from collections.abc). +- "AbstractSet", # collections.abc.Set. +- "ByteString", +- "Container", ++ 'AbstractSet', # collections.abc.Set. ++ 'ByteString', ++ 'Container', ++ + # Concrete collection types. +- "Counter", +- "Deque", +- "Dict", +- "DefaultDict", +- "List", +- "Set", +- "FrozenSet", +- "NamedTuple", # Not really a type. +- "Generator", ++ 'Counter', ++ 'Deque', ++ 'Dict', ++ 'DefaultDict', ++ 'List', ++ 'Set', ++ 'FrozenSet', ++ 'NamedTuple', # Not really a type. ++ 'Generator', + ] + + not_shareables = [ + # singletons + True, + False, +- NotImplemented, +- ..., ++ NotImplemented, ..., + # builtin types and objects + type, + object, +@@ -48,20 +49,23 @@ + SubBytes(b"spam"), + ] + +-if "PYTHON" in os.environ: ++if 'PYTHON' in os.environ: + add_compiler(compiler_from_env()) + else: + # for compiler in compilers.values(): +- # add_compiler(compiler) ++ # add_compiler(compiler) + add_compiler(compilers[(7.0, 32)]) + # add_compiler(compilers[(7.1, 64)]) +- + + # Comment before function. + def inline_comments_in_brackets_ruin_everything(): + if typedargslist: +- parameters.children = [children[0], body, children[-1]] # (1 # )1 + parameters.children = [ ++ children[0], # (1 ++ body, ++ children[-1] # )1 ++ ] ++ parameters.children = [ + children[0], body, + children[-1], # type: ignore +@@ -73,49 +77,42 @@ parameters.children[-1], # )2 ] -- parameters.children = [parameters.what_if_this_was_actually_long.children[0], body, parameters.children[-1]] # type: ignore -+ parameters.children = [ -+ parameters.what_if_this_was_actually_long.children[0], -+ body, -+ parameters.children[-1], -+ ] # type: ignore - if ( - self._proc is not None - # has the child process finished? -@@ -103,35 +107,35 @@ + parameters.children = [parameters.what_if_this_was_actually_long.children[0], body, parameters.children[-1]] # type: ignore +- if ( +- self._proc is not None +- # has the child process finished? +- and self._returncode is None +- # the child process has finished, but the +- # transport hasn't been notified yet? +- and self._proc.poll() is None +- ): ++ if (self._proc is not None ++ # has the child process finished? ++ and self._returncode is None ++ # the child process has finished, but the ++ # transport hasn't been notified yet? ++ and self._proc.poll() is None): + pass + # no newline before or after + short = [ +- # one +- 1, +- # two +- 2, +- ] ++ # one ++ 1, ++ # two ++ 2] + + # no newline after +- call( +- arg1, +- arg2, +- """ ++ call(arg1, arg2, """ + short +-""", +- arg3=True, +- ) ++""", arg3=True) + ############################################################################ call2( - # short -+ #short - arg1, +- arg1, - # but -+ #but - arg2, +- arg2, - # multiline -+ #multiline - """ +- """ ++ #short ++ arg1, ++ #but ++ arg2, ++ #multiline ++ """ short """, - # yup - arg3=True, - ) -- lcomp = [ +- # yup +- arg3=True, +- ) ++ # yup ++ arg3=True) + lcomp = [ - element for element in collection if element is not None # yup # yup # right -- ] -+ lcomp = [element for element in collection if element is not None] # yup # yup # right ++ element # yup ++ for element in collection # yup ++ if element is not None # right + ] lcomp2 = [ # hello - element -- # yup -- for element in collection -- # right -- if element is not None -+ for # yup -+ element in collection -+ if # right -+ element -+ is not None +@@ -127,7 +124,7 @@ ] lcomp3 = [ # This one is actually too long to fit in a single line. - element.split("\n", 1)[0] -- # yup -- for element in collection.select_elements() -- # right -- if element is not None -+ for # yup -+ element in collection.select_elements() -+ if # right -+ element -+ is not None - ] - while True: - if False: -@@ -167,7 +171,7 @@ +- element.split("\n", 1)[0] ++ element.split('\n', 1)[0] + # yup + for element in collection.select_elements() + # right +@@ -140,34 +137,26 @@ + # and round and round we go + # and round and round we go + +- # let's return ++ # let's return + return Node( + syms.simple_stmt, +- [Node(statement, result), Leaf(token.NEWLINE, "\n")], # FIXME: \r\n? ++ [ ++ Node(statement, result), ++ Leaf(token.NEWLINE, '\n') # FIXME: \r\n? ++ ], + ) + +- +-CONFIG_FILES = ( +- [ +- CONFIG_FILE, +- ] +- + SHARED_CONFIG_FILES +- + USER_CONFIG_FILES +-) # type: Final ++CONFIG_FILES = [CONFIG_FILE, ] + SHARED_CONFIG_FILES + USER_CONFIG_FILES # type: Final + +- + class Test: + def _init_host(self, parsed) -> None: +- if parsed.hostname is None or not parsed.hostname.strip(): # type: ignore ++ if (parsed.hostname is None or # type: ignore ++ not parsed.hostname.strip()): + pass + +- + ####################### + ### SECTION COMMENT ### ####################### -instruction() # comment with bad spacing -+instruction() #comment with bad spacing - - # END COMMENTS - # MORE END COMMENTS +- +-# END COMMENTS +-# MORE END COMMENTS ++instruction()#comment with bad spacing ``` ## Ruff Output ```py from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( - MyLovelyCompanyTeamProjectComponent, # NOT DRY + MyLovelyCompanyTeamProjectComponent # NOT DRY ) from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( - MyLovelyCompanyTeamProjectComponent as component, # DRY + MyLovelyCompanyTeamProjectComponent as component # DRY ) # Please keep __all__ alphabetized within each category. __all__ = [ # Super-special typing primitives. - "Any", - "Callable", - "ClassVar", + 'Any', + 'Callable', + 'ClassVar', + # ABCs (from collections.abc). - "AbstractSet", # collections.abc.Set. - "ByteString", - "Container", + 'AbstractSet', # collections.abc.Set. + 'ByteString', + 'Container', + # Concrete collection types. - "Counter", - "Deque", - "Dict", - "DefaultDict", - "List", - "Set", - "FrozenSet", - "NamedTuple", # Not really a type. - "Generator", + 'Counter', + 'Deque', + 'Dict', + 'DefaultDict', + 'List', + 'Set', + 'FrozenSet', + 'NamedTuple', # Not really a type. + 'Generator', ] not_shareables = [ # singletons True, False, - NotImplemented, - ..., + NotImplemented, ..., # builtin types and objects type, object, @@ -305,19 +448,22 @@ not_shareables = [ SubBytes(b"spam"), ] -if "PYTHON" in os.environ: +if 'PYTHON' in os.environ: add_compiler(compiler_from_env()) else: # for compiler in compilers.values(): - # add_compiler(compiler) + # add_compiler(compiler) add_compiler(compilers[(7.0, 32)]) # add_compiler(compilers[(7.1, 64)]) - # Comment before function. def inline_comments_in_brackets_ruin_everything(): if typedargslist: - parameters.children = [children[0], body, children[-1]] # (1 # )1 + parameters.children = [ + children[0], # (1 + body, + children[-1] # )1 + ] parameters.children = [ children[0], body, @@ -329,70 +475,59 @@ def inline_comments_in_brackets_ruin_everything(): body, parameters.children[-1], # )2 ] - parameters.children = [ - parameters.what_if_this_was_actually_long.children[0], - body, - parameters.children[-1], - ] # type: ignore - if ( - self._proc is not None - # has the child process finished? - and self._returncode is None - # the child process has finished, but the - # transport hasn't been notified yet? - and self._proc.poll() is None - ): + parameters.children = [parameters.what_if_this_was_actually_long.children[0], body, parameters.children[-1]] # type: ignore + if (self._proc is not None + # has the child process finished? + and self._returncode is None + # the child process has finished, but the + # transport hasn't been notified yet? + and self._proc.poll() is None): pass # no newline before or after short = [ - # one - 1, - # two - 2, - ] + # one + 1, + # two + 2] # no newline after - call( - arg1, - arg2, - """ + call(arg1, arg2, """ short -""", - arg3=True, - ) +""", arg3=True) ############################################################################ call2( - #short - arg1, - #but - arg2, - #multiline - """ + #short + arg1, + #but + arg2, + #multiline + """ short """, - # yup - arg3=True, - ) - lcomp = [element for element in collection if element is not None] # yup # yup # right + # yup + arg3=True) + lcomp = [ + element # yup + for element in collection # yup + if element is not None # right + ] lcomp2 = [ # hello element - for # yup - element in collection - if # right - element - is not None + # yup + for element in collection + # right + if element is not None ] lcomp3 = [ # This one is actually too long to fit in a single line. - element.split("\n", 1)[0] - for # yup - element in collection.select_elements() - if # right - element - is not None + element.split('\n', 1)[0] + # yup + for element in collection.select_elements() + # right + if element is not None ] while True: if False: @@ -401,37 +536,29 @@ short # and round and round we go # and round and round we go - # let's return + # let's return return Node( syms.simple_stmt, - [Node(statement, result), Leaf(token.NEWLINE, "\n")], # FIXME: \r\n? + [ + Node(statement, result), + Leaf(token.NEWLINE, '\n') # FIXME: \r\n? + ], ) - -CONFIG_FILES = ( - [ - CONFIG_FILE, - ] - + SHARED_CONFIG_FILES - + USER_CONFIG_FILES -) # type: Final - +CONFIG_FILES = [CONFIG_FILE, ] + SHARED_CONFIG_FILES + USER_CONFIG_FILES # type: Final class Test: def _init_host(self, parsed) -> None: - if parsed.hostname is None or not parsed.hostname.strip(): # type: ignore + if (parsed.hostname is None or # type: ignore + not parsed.hostname.strip()): pass - ####################### ### SECTION COMMENT ### ####################### -instruction() #comment with bad spacing - -# END COMMENTS -# MORE END COMMENTS +instruction()#comment with bad spacing ``` ## Black Output diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments3_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments3_py.snap deleted file mode 100644 index 69439b5d70397..0000000000000 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments3_py.snap +++ /dev/null @@ -1,189 +0,0 @@ ---- -source: crates/ruff_python_formatter/src/lib.rs -expression: snapshot -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments3.py ---- -## Input - -```py -# The percent-percent comments are Spyder IDE cells. - - -# %% -def func(): - x = """ - a really long string - """ - lcomp3 = [ - # This one is actually too long to fit in a single line. - element.split("\n", 1)[0] - # yup - for element in collection.select_elements() - # right - if element is not None - ] - # Capture each of the exceptions in the MultiError along with each of their causes and contexts - if isinstance(exc_value, MultiError): - embedded = [] - for exc in exc_value.exceptions: - if exc not in _seen: - embedded.append( - # This should be left alone (before) - traceback.TracebackException.from_exception( - exc, - limit=limit, - lookup_lines=lookup_lines, - capture_locals=capture_locals, - # copy the set of _seen exceptions so that duplicates - # shared between sub-exceptions are not omitted - _seen=set(_seen), - ) - # This should be left alone (after) - ) - - # everything is fine if the expression isn't nested - traceback.TracebackException.from_exception( - exc, - limit=limit, - lookup_lines=lookup_lines, - capture_locals=capture_locals, - # copy the set of _seen exceptions so that duplicates - # shared between sub-exceptions are not omitted - _seen=set(_seen), - ) - - -# %% -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -9,10 +9,11 @@ - lcomp3 = [ - # This one is actually too long to fit in a single line. - element.split("\n", 1)[0] -- # yup -- for element in collection.select_elements() -- # right -- if element is not None -+ for # yup -+ element in collection.select_elements() -+ if # right -+ element -+ is not None - ] - # Capture each of the exceptions in the MultiError along with each of their causes and contexts - if isinstance(exc_value, MultiError): -``` - -## Ruff Output - -```py -# The percent-percent comments are Spyder IDE cells. - - -# %% -def func(): - x = """ - a really long string - """ - lcomp3 = [ - # This one is actually too long to fit in a single line. - element.split("\n", 1)[0] - for # yup - element in collection.select_elements() - if # right - element - is not None - ] - # Capture each of the exceptions in the MultiError along with each of their causes and contexts - if isinstance(exc_value, MultiError): - embedded = [] - for exc in exc_value.exceptions: - if exc not in _seen: - embedded.append( - # This should be left alone (before) - traceback.TracebackException.from_exception( - exc, - limit=limit, - lookup_lines=lookup_lines, - capture_locals=capture_locals, - # copy the set of _seen exceptions so that duplicates - # shared between sub-exceptions are not omitted - _seen=set(_seen), - ) - # This should be left alone (after) - ) - - # everything is fine if the expression isn't nested - traceback.TracebackException.from_exception( - exc, - limit=limit, - lookup_lines=lookup_lines, - capture_locals=capture_locals, - # copy the set of _seen exceptions so that duplicates - # shared between sub-exceptions are not omitted - _seen=set(_seen), - ) - - -# %% -``` - -## Black Output - -```py -# The percent-percent comments are Spyder IDE cells. - - -# %% -def func(): - x = """ - a really long string - """ - lcomp3 = [ - # This one is actually too long to fit in a single line. - element.split("\n", 1)[0] - # yup - for element in collection.select_elements() - # right - if element is not None - ] - # Capture each of the exceptions in the MultiError along with each of their causes and contexts - if isinstance(exc_value, MultiError): - embedded = [] - for exc in exc_value.exceptions: - if exc not in _seen: - embedded.append( - # This should be left alone (before) - traceback.TracebackException.from_exception( - exc, - limit=limit, - lookup_lines=lookup_lines, - capture_locals=capture_locals, - # copy the set of _seen exceptions so that duplicates - # shared between sub-exceptions are not omitted - _seen=set(_seen), - ) - # This should be left alone (after) - ) - - # everything is fine if the expression isn't nested - traceback.TracebackException.from_exception( - exc, - limit=limit, - lookup_lines=lookup_lines, - capture_locals=capture_locals, - # copy the set of _seen exceptions so that duplicates - # shared between sub-exceptions are not omitted - _seen=set(_seen), - ) - - -# %% -``` - - diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments4_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments4_py.snap deleted file mode 100644 index d246955bc6bef..0000000000000 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments4_py.snap +++ /dev/null @@ -1,435 +0,0 @@ ---- -source: crates/ruff_python_formatter/src/lib.rs -expression: snapshot -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments4.py ---- -## Input - -```py -from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( - MyLovelyCompanyTeamProjectComponent, # NOT DRY -) -from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( - MyLovelyCompanyTeamProjectComponent as component, # DRY -) - - -class C: - @pytest.mark.parametrize( - ("post_data", "message"), - [ - # metadata_version errors. - ( - {}, - "None is an invalid value for Metadata-Version. Error: This field is" - " required. see" - " https://packaging.python.org/specifications/core-metadata", - ), - ( - {"metadata_version": "-1"}, - "'-1' is an invalid value for Metadata-Version. Error: Unknown Metadata" - " Version see" - " https://packaging.python.org/specifications/core-metadata", - ), - # name errors. - ( - {"metadata_version": "1.2"}, - "'' is an invalid value for Name. Error: This field is required. see" - " https://packaging.python.org/specifications/core-metadata", - ), - ( - {"metadata_version": "1.2", "name": "foo-"}, - "'foo-' is an invalid value for Name. Error: Must start and end with a" - " letter or numeral and contain only ascii numeric and '.', '_' and" - " '-'. see https://packaging.python.org/specifications/core-metadata", - ), - # version errors. - ( - {"metadata_version": "1.2", "name": "example"}, - "'' is an invalid value for Version. Error: This field is required. see" - " https://packaging.python.org/specifications/core-metadata", - ), - ( - {"metadata_version": "1.2", "name": "example", "version": "dog"}, - "'dog' is an invalid value for Version. Error: Must start and end with" - " a letter or numeral and contain only ascii numeric and '.', '_' and" - " '-'. see https://packaging.python.org/specifications/core-metadata", - ), - ], - ) - def test_fails_invalid_post_data( - self, pyramid_config, db_request, post_data, message - ): - pyramid_config.testing_securitypolicy(userid=1) - db_request.POST = MultiDict(post_data) - - -def foo(list_a, list_b): - results = ( - User.query.filter(User.foo == "bar") - .filter( # Because foo. - db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) - ) - .filter(User.xyz.is_(None)) - # Another comment about the filtering on is_quux goes here. - .filter(db.not_(User.is_pending.astext.cast(db.Boolean).is_(True))) - .order_by(User.created_at.desc()) - .with_for_update(key_share=True) - .all() - ) - return results - - -def foo2(list_a, list_b): - # Standalone comment reasonably placed. - return ( - User.query.filter(User.foo == "bar") - .filter( - db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) - ) - .filter(User.xyz.is_(None)) - ) - - -def foo3(list_a, list_b): - return ( - # Standalone comment but weirdly placed. - User.query.filter(User.foo == "bar") - .filter( - db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) - ) - .filter(User.xyz.is_(None)) - ) -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -10,39 +10,50 @@ - @pytest.mark.parametrize( - ("post_data", "message"), - [ -- # metadata_version errors. -- ( -+ (# metadata_version errors. - {}, - "None is an invalid value for Metadata-Version. Error: This field is" - " required. see" - " https://packaging.python.org/specifications/core-metadata", - ), - ( -- {"metadata_version": "-1"}, -+ { -+ "metadata_version": "-1", -+ }, - "'-1' is an invalid value for Metadata-Version. Error: Unknown Metadata" - " Version see" - " https://packaging.python.org/specifications/core-metadata", - ), -- # name errors. -- ( -- {"metadata_version": "1.2"}, -+ (# name errors. -+ { -+ "metadata_version": "1.2", -+ }, - "'' is an invalid value for Name. Error: This field is required. see" - " https://packaging.python.org/specifications/core-metadata", - ), - ( -- {"metadata_version": "1.2", "name": "foo-"}, -+ { -+ "metadata_version": "1.2", -+ "name": "foo-", -+ }, - "'foo-' is an invalid value for Name. Error: Must start and end with a" - " letter or numeral and contain only ascii numeric and '.', '_' and" - " '-'. see https://packaging.python.org/specifications/core-metadata", - ), -- # version errors. -- ( -- {"metadata_version": "1.2", "name": "example"}, -+ (# version errors. -+ { -+ "metadata_version": "1.2", -+ "name": "example", -+ }, - "'' is an invalid value for Version. Error: This field is required. see" - " https://packaging.python.org/specifications/core-metadata", - ), - ( -- {"metadata_version": "1.2", "name": "example", "version": "dog"}, -+ { -+ "metadata_version": "1.2", -+ "name": "example", -+ "version": "dog", -+ }, - "'dog' is an invalid value for Version. Error: Must start and end with" - " a letter or numeral and contain only ascii numeric and '.', '_' and" - " '-'. see https://packaging.python.org/specifications/core-metadata", -@@ -50,45 +61,34 @@ - ], - ) - def test_fails_invalid_post_data( -- self, pyramid_config, db_request, post_data, message -+ self, -+ pyramid_config, -+ db_request, -+ post_data, -+ message, - ): - pyramid_config.testing_securitypolicy(userid=1) - db_request.POST = MultiDict(post_data) - - - def foo(list_a, list_b): -- results = ( -- User.query.filter(User.foo == "bar") -- .filter( # Because foo. -- db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) -- ) -- .filter(User.xyz.is_(None)) -- # Another comment about the filtering on is_quux goes here. -- .filter(db.not_(User.is_pending.astext.cast(db.Boolean).is_(True))) -- .order_by(User.created_at.desc()) -- .with_for_update(key_share=True) -- .all() -- ) -+ results = User.query.filter(User.foo == "bar").filter( # Because foo. -+ db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) -+ ).filter(User.xyz.is_(None)).filter( -+ db.not_(User.is_pending.astext.cast(db.Boolean).is_(True)) -+ ).order_by(User.created_at.desc()).with_for_update(key_share=True).all() - return results - - - def foo2(list_a, list_b): - # Standalone comment reasonably placed. -- return ( -- User.query.filter(User.foo == "bar") -- .filter( -- db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) -- ) -- .filter(User.xyz.is_(None)) -- ) -+ return User.query.filter(User.foo == "bar").filter( -+ db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) -+ ).filter(User.xyz.is_(None)) - - - def foo3(list_a, list_b): -- return ( -- # Standalone comment but weirdly placed. -- User.query.filter(User.foo == "bar") -- .filter( -- db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) -- ) -- .filter(User.xyz.is_(None)) -- ) -+ return # Standalone comment but weirdly placed. -+ User.query.filter(User.foo == "bar").filter( -+ db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) -+ ).filter(User.xyz.is_(None)) -``` - -## Ruff Output - -```py -from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( - MyLovelyCompanyTeamProjectComponent, # NOT DRY -) -from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( - MyLovelyCompanyTeamProjectComponent as component, # DRY -) - - -class C: - @pytest.mark.parametrize( - ("post_data", "message"), - [ - (# metadata_version errors. - {}, - "None is an invalid value for Metadata-Version. Error: This field is" - " required. see" - " https://packaging.python.org/specifications/core-metadata", - ), - ( - { - "metadata_version": "-1", - }, - "'-1' is an invalid value for Metadata-Version. Error: Unknown Metadata" - " Version see" - " https://packaging.python.org/specifications/core-metadata", - ), - (# name errors. - { - "metadata_version": "1.2", - }, - "'' is an invalid value for Name. Error: This field is required. see" - " https://packaging.python.org/specifications/core-metadata", - ), - ( - { - "metadata_version": "1.2", - "name": "foo-", - }, - "'foo-' is an invalid value for Name. Error: Must start and end with a" - " letter or numeral and contain only ascii numeric and '.', '_' and" - " '-'. see https://packaging.python.org/specifications/core-metadata", - ), - (# version errors. - { - "metadata_version": "1.2", - "name": "example", - }, - "'' is an invalid value for Version. Error: This field is required. see" - " https://packaging.python.org/specifications/core-metadata", - ), - ( - { - "metadata_version": "1.2", - "name": "example", - "version": "dog", - }, - "'dog' is an invalid value for Version. Error: Must start and end with" - " a letter or numeral and contain only ascii numeric and '.', '_' and" - " '-'. see https://packaging.python.org/specifications/core-metadata", - ), - ], - ) - def test_fails_invalid_post_data( - self, - pyramid_config, - db_request, - post_data, - message, - ): - pyramid_config.testing_securitypolicy(userid=1) - db_request.POST = MultiDict(post_data) - - -def foo(list_a, list_b): - results = User.query.filter(User.foo == "bar").filter( # Because foo. - db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) - ).filter(User.xyz.is_(None)).filter( - db.not_(User.is_pending.astext.cast(db.Boolean).is_(True)) - ).order_by(User.created_at.desc()).with_for_update(key_share=True).all() - return results - - -def foo2(list_a, list_b): - # Standalone comment reasonably placed. - return User.query.filter(User.foo == "bar").filter( - db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) - ).filter(User.xyz.is_(None)) - - -def foo3(list_a, list_b): - return # Standalone comment but weirdly placed. - User.query.filter(User.foo == "bar").filter( - db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) - ).filter(User.xyz.is_(None)) -``` - -## Black Output - -```py -from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( - MyLovelyCompanyTeamProjectComponent, # NOT DRY -) -from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( - MyLovelyCompanyTeamProjectComponent as component, # DRY -) - - -class C: - @pytest.mark.parametrize( - ("post_data", "message"), - [ - # metadata_version errors. - ( - {}, - "None is an invalid value for Metadata-Version. Error: This field is" - " required. see" - " https://packaging.python.org/specifications/core-metadata", - ), - ( - {"metadata_version": "-1"}, - "'-1' is an invalid value for Metadata-Version. Error: Unknown Metadata" - " Version see" - " https://packaging.python.org/specifications/core-metadata", - ), - # name errors. - ( - {"metadata_version": "1.2"}, - "'' is an invalid value for Name. Error: This field is required. see" - " https://packaging.python.org/specifications/core-metadata", - ), - ( - {"metadata_version": "1.2", "name": "foo-"}, - "'foo-' is an invalid value for Name. Error: Must start and end with a" - " letter or numeral and contain only ascii numeric and '.', '_' and" - " '-'. see https://packaging.python.org/specifications/core-metadata", - ), - # version errors. - ( - {"metadata_version": "1.2", "name": "example"}, - "'' is an invalid value for Version. Error: This field is required. see" - " https://packaging.python.org/specifications/core-metadata", - ), - ( - {"metadata_version": "1.2", "name": "example", "version": "dog"}, - "'dog' is an invalid value for Version. Error: Must start and end with" - " a letter or numeral and contain only ascii numeric and '.', '_' and" - " '-'. see https://packaging.python.org/specifications/core-metadata", - ), - ], - ) - def test_fails_invalid_post_data( - self, pyramid_config, db_request, post_data, message - ): - pyramid_config.testing_securitypolicy(userid=1) - db_request.POST = MultiDict(post_data) - - -def foo(list_a, list_b): - results = ( - User.query.filter(User.foo == "bar") - .filter( # Because foo. - db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) - ) - .filter(User.xyz.is_(None)) - # Another comment about the filtering on is_quux goes here. - .filter(db.not_(User.is_pending.astext.cast(db.Boolean).is_(True))) - .order_by(User.created_at.desc()) - .with_for_update(key_share=True) - .all() - ) - return results - - -def foo2(list_a, list_b): - # Standalone comment reasonably placed. - return ( - User.query.filter(User.foo == "bar") - .filter( - db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) - ) - .filter(User.xyz.is_(None)) - ) - - -def foo3(list_a, list_b): - return ( - # Standalone comment but weirdly placed. - User.query.filter(User.foo == "bar") - .filter( - db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b)) - ) - .filter(User.xyz.is_(None)) - ) -``` - - diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments5_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments5_py.snap deleted file mode 100644 index 32a6cb310cf47..0000000000000 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments5_py.snap +++ /dev/null @@ -1,279 +0,0 @@ ---- -source: crates/ruff_python_formatter/src/lib.rs -expression: snapshot -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments5.py ---- -## Input - -```py -while True: - if something.changed: - do.stuff() # trailing comment - # Comment belongs to the `if` block. - # This one belongs to the `while` block. - - # Should this one, too? I guess so. - -# This one is properly standalone now. - -for i in range(100): - # first we do this - if i % 33 == 0: - break - - # then we do this - print(i) - # and finally we loop around - -with open(some_temp_file) as f: - data = f.read() - -try: - with open(some_other_file) as w: - w.write(data) - -except OSError: - print("problems") - -import sys - - -# leading function comment -def wat(): - ... - # trailing function comment - - -# SECTION COMMENT - - -# leading 1 -@deco1 -# leading 2 -@deco2(with_args=True) -# leading 3 -@deco3 -def decorated1(): - ... - - -# leading 1 -@deco1 -# leading 2 -@deco2(with_args=True) -# leading function comment -def decorated1(): - ... - - -# Note: this is fixed in -# Preview.empty_lines_before_class_or_def_with_leading_comments. -# In the current style, the user will have to split those lines by hand. -some_instruction - - -# This comment should be split from `some_instruction` by two lines but isn't. -def g(): - ... - - -if __name__ == "__main__": - main() -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -23,7 +23,6 @@ - try: - with open(some_other_file) as w: - w.write(data) -- - except OSError: - print("problems") - -@@ -41,18 +40,18 @@ - - # leading 1 - @deco1 --# leading 2 --@deco2(with_args=True) --# leading 3 --@deco3 -+@# leading 2 -+deco2(with_args=True) -+@# leading 3 -+deco3 - def decorated1(): - ... - - - # leading 1 - @deco1 --# leading 2 --@deco2(with_args=True) -+@# leading 2 -+deco2(with_args=True) - # leading function comment - def decorated1(): - ... -``` - -## Ruff Output - -```py -while True: - if something.changed: - do.stuff() # trailing comment - # Comment belongs to the `if` block. - # This one belongs to the `while` block. - - # Should this one, too? I guess so. - -# This one is properly standalone now. - -for i in range(100): - # first we do this - if i % 33 == 0: - break - - # then we do this - print(i) - # and finally we loop around - -with open(some_temp_file) as f: - data = f.read() - -try: - with open(some_other_file) as w: - w.write(data) -except OSError: - print("problems") - -import sys - - -# leading function comment -def wat(): - ... - # trailing function comment - - -# SECTION COMMENT - - -# leading 1 -@deco1 -@# leading 2 -deco2(with_args=True) -@# leading 3 -deco3 -def decorated1(): - ... - - -# leading 1 -@deco1 -@# leading 2 -deco2(with_args=True) -# leading function comment -def decorated1(): - ... - - -# Note: this is fixed in -# Preview.empty_lines_before_class_or_def_with_leading_comments. -# In the current style, the user will have to split those lines by hand. -some_instruction - - -# This comment should be split from `some_instruction` by two lines but isn't. -def g(): - ... - - -if __name__ == "__main__": - main() -``` - -## Black Output - -```py -while True: - if something.changed: - do.stuff() # trailing comment - # Comment belongs to the `if` block. - # This one belongs to the `while` block. - - # Should this one, too? I guess so. - -# This one is properly standalone now. - -for i in range(100): - # first we do this - if i % 33 == 0: - break - - # then we do this - print(i) - # and finally we loop around - -with open(some_temp_file) as f: - data = f.read() - -try: - with open(some_other_file) as w: - w.write(data) - -except OSError: - print("problems") - -import sys - - -# leading function comment -def wat(): - ... - # trailing function comment - - -# SECTION COMMENT - - -# leading 1 -@deco1 -# leading 2 -@deco2(with_args=True) -# leading 3 -@deco3 -def decorated1(): - ... - - -# leading 1 -@deco1 -# leading 2 -@deco2(with_args=True) -# leading function comment -def decorated1(): - ... - - -# Note: this is fixed in -# Preview.empty_lines_before_class_or_def_with_leading_comments. -# In the current style, the user will have to split those lines by hand. -some_instruction - - -# This comment should be split from `some_instruction` by two lines but isn't. -def g(): - ... - - -if __name__ == "__main__": - main() -``` - - 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 deleted file mode 100644 index 5f3625babfc8b..0000000000000 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments6_py.snap +++ /dev/null @@ -1,456 +0,0 @@ ---- -source: crates/ruff_python_formatter/src/lib.rs -expression: snapshot -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments6.py ---- -## Input - -```py -from typing import Any, Tuple - - -def f( - a, # type: int -): - pass - - -# test type comments -def f(a, b, c, d, e, f, g, h, i): - # type: (int, int, int, int, int, int, int, int, int) -> None - pass - - -def f( - a, # type: int - b, # type: int - c, # type: int - d, # type: int - e, # type: int - f, # type: int - g, # type: int - h, # type: int - i, # type: int -): - # type: (...) -> None - pass - - -def f( - arg, # type: int - *args, # type: *Any - default=False, # type: bool - **kwargs, # type: **Any -): - # type: (...) -> None - pass - - -def f( - a, # type: int - b, # type: int - c, # type: int - d, # type: int -): - # type: (...) -> None - - element = 0 # type: int - another_element = 1 # type: float - another_element_with_long_name = 2 # type: int - another_really_really_long_element_with_a_unnecessarily_long_name_to_describe_what_it_does_enterprise_style = ( - 3 - ) # type: int - an_element_with_a_long_value = calls() or more_calls() and more() # type: bool - - tup = ( - another_element, - another_really_really_long_element_with_a_unnecessarily_long_name_to_describe_what_it_does_enterprise_style, - ) # type: Tuple[int, int] - - a = ( - element - + another_element - + another_element_with_long_name - + element - + another_element - + another_element_with_long_name - ) # type: int - - -def f( - x, # not a type comment - y, # type: int -): - # type: (...) -> None - pass - - -def f( - x, # not a type comment -): # type: (int) -> None - pass - - -def func( - a=some_list[0], # type: int -): # type: () -> int - c = call( - 0.0123, - 0.0456, - 0.0789, - 0.0123, - 0.0456, - 0.0789, - 0.0123, - 0.0456, - 0.0789, - a[-1], # type: ignore - ) - - c = call( - "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa" # type: ignore - ) - - -result = ( # aaa - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -) - -AAAAAAAAAAAAA = [AAAAAAAAAAAAA] + SHARED_AAAAAAAAAAAAA + USER_AAAAAAAAAAAAA + AAAAAAAAAAAAA # type: ignore - -call_to_some_function_asdf( - foo, - [AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, BBBBBBBBBBBB], # type: ignore -) - -aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*items))) # type: ignore[arg-type] -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -31,8 +31,8 @@ - def f( - arg, # type: int - *args, # type: *Any -- default=False, # type: bool -- **kwargs, # type: **Any -+ default=False, # type: bool # type: **Any -+ **kwargs, - ): - # type: (...) -> None - pass -@@ -49,9 +49,7 @@ - element = 0 # type: int - another_element = 1 # type: float - another_element_with_long_name = 2 # type: int -- another_really_really_long_element_with_a_unnecessarily_long_name_to_describe_what_it_does_enterprise_style = ( -- 3 -- ) # type: int -+ another_really_really_long_element_with_a_unnecessarily_long_name_to_describe_what_it_does_enterprise_style = 3 # type: int - an_element_with_a_long_value = calls() or more_calls() and more() # type: bool - - tup = ( -@@ -100,19 +98,33 @@ - ) - - c = call( -- "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa" # type: ignore -+ "aaaaaaaa", -+ "aaaaaaaa", -+ "aaaaaaaa", -+ "aaaaaaaa", -+ "aaaaaaaa", -+ "aaaaaaaa", -+ "aaaaaaaa", # type: ignore - ) - - --result = ( # aaa -- "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" --) -+result = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # aaa - --AAAAAAAAAAAAA = [AAAAAAAAAAAAA] + SHARED_AAAAAAAAAAAAA + USER_AAAAAAAAAAAAA + AAAAAAAAAAAAA # type: ignore -+AAAAAAAAAAAAA = ( -+ [AAAAAAAAAAAAA] -+ + SHARED_AAAAAAAAAAAAA -+ + USER_AAAAAAAAAAAAA -+ + AAAAAAAAAAAAA -+) # type: ignore - - call_to_some_function_asdf( - foo, -- [AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, BBBBBBBBBBBB], # type: ignore -+ [ -+ AAAAAAAAAAAAAAAAAAAAAAA, -+ AAAAAAAAAAAAAAAAAAAAAAA, -+ AAAAAAAAAAAAAAAAAAAAAAA, -+ BBBBBBBBBBBB, -+ ], # type: ignore - ) - - aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*items))) # type: ignore[arg-type] -``` - -## Ruff Output - -```py -from typing import Any, Tuple - - -def f( - a, # type: int -): - pass - - -# test type comments -def f(a, b, c, d, e, f, g, h, i): - # type: (int, int, int, int, int, int, int, int, int) -> None - pass - - -def f( - a, # type: int - b, # type: int - c, # type: int - d, # type: int - e, # type: int - f, # type: int - g, # type: int - h, # type: int - i, # type: int -): - # type: (...) -> None - pass - - -def f( - arg, # type: int - *args, # type: *Any - default=False, # type: bool # type: **Any - **kwargs, -): - # type: (...) -> None - pass - - -def f( - a, # type: int - b, # type: int - c, # type: int - d, # type: int -): - # type: (...) -> None - - element = 0 # type: int - another_element = 1 # type: float - another_element_with_long_name = 2 # type: int - another_really_really_long_element_with_a_unnecessarily_long_name_to_describe_what_it_does_enterprise_style = 3 # type: int - an_element_with_a_long_value = calls() or more_calls() and more() # type: bool - - tup = ( - another_element, - another_really_really_long_element_with_a_unnecessarily_long_name_to_describe_what_it_does_enterprise_style, - ) # type: Tuple[int, int] - - a = ( - element - + another_element - + another_element_with_long_name - + element - + another_element - + another_element_with_long_name - ) # type: int - - -def f( - x, # not a type comment - y, # type: int -): - # type: (...) -> None - pass - - -def f( - x, # not a type comment -): # type: (int) -> None - pass - - -def func( - a=some_list[0], # type: int -): # type: () -> int - c = call( - 0.0123, - 0.0456, - 0.0789, - 0.0123, - 0.0456, - 0.0789, - 0.0123, - 0.0456, - 0.0789, - a[-1], # type: ignore - ) - - c = call( - "aaaaaaaa", - "aaaaaaaa", - "aaaaaaaa", - "aaaaaaaa", - "aaaaaaaa", - "aaaaaaaa", - "aaaaaaaa", # type: ignore - ) - - -result = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # aaa - -AAAAAAAAAAAAA = ( - [AAAAAAAAAAAAA] - + SHARED_AAAAAAAAAAAAA - + USER_AAAAAAAAAAAAA - + AAAAAAAAAAAAA -) # type: ignore - -call_to_some_function_asdf( - foo, - [ - AAAAAAAAAAAAAAAAAAAAAAA, - AAAAAAAAAAAAAAAAAAAAAAA, - AAAAAAAAAAAAAAAAAAAAAAA, - BBBBBBBBBBBB, - ], # type: ignore -) - -aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*items))) # type: ignore[arg-type] -``` - -## Black Output - -```py -from typing import Any, Tuple - - -def f( - a, # type: int -): - pass - - -# test type comments -def f(a, b, c, d, e, f, g, h, i): - # type: (int, int, int, int, int, int, int, int, int) -> None - pass - - -def f( - a, # type: int - b, # type: int - c, # type: int - d, # type: int - e, # type: int - f, # type: int - g, # type: int - h, # type: int - i, # type: int -): - # type: (...) -> None - pass - - -def f( - arg, # type: int - *args, # type: *Any - default=False, # type: bool - **kwargs, # type: **Any -): - # type: (...) -> None - pass - - -def f( - a, # type: int - b, # type: int - c, # type: int - d, # type: int -): - # type: (...) -> None - - element = 0 # type: int - another_element = 1 # type: float - another_element_with_long_name = 2 # type: int - another_really_really_long_element_with_a_unnecessarily_long_name_to_describe_what_it_does_enterprise_style = ( - 3 - ) # type: int - an_element_with_a_long_value = calls() or more_calls() and more() # type: bool - - tup = ( - another_element, - another_really_really_long_element_with_a_unnecessarily_long_name_to_describe_what_it_does_enterprise_style, - ) # type: Tuple[int, int] - - a = ( - element - + another_element - + another_element_with_long_name - + element - + another_element - + another_element_with_long_name - ) # type: int - - -def f( - x, # not a type comment - y, # type: int -): - # type: (...) -> None - pass - - -def f( - x, # not a type comment -): # type: (int) -> None - pass - - -def func( - a=some_list[0], # type: int -): # type: () -> int - c = call( - 0.0123, - 0.0456, - 0.0789, - 0.0123, - 0.0456, - 0.0789, - 0.0123, - 0.0456, - 0.0789, - a[-1], # type: ignore - ) - - c = call( - "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa" # type: ignore - ) - - -result = ( # aaa - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -) - -AAAAAAAAAAAAA = [AAAAAAAAAAAAA] + SHARED_AAAAAAAAAAAAA + USER_AAAAAAAAAAAAA + AAAAAAAAAAAAA # type: ignore - -call_to_some_function_asdf( - foo, - [AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, AAAAAAAAAAAAAAAAAAAAAAA, BBBBBBBBBBBB], # type: ignore -) - -aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*items))) # type: ignore[arg-type] -``` - - diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments8_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments8_py.snap new file mode 100644 index 0000000000000..2fcf94c83ddc2 --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments8_py.snap @@ -0,0 +1,47 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments8.py +--- +## Input + +```py +# The percent-percent comments are Spyder IDE cells. +# Both `#%%`` and `# %%` are accepted, so `black` standardises +# to the latter. + +#%% +# %% +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,6 +0,0 @@ +-# The percent-percent comments are Spyder IDE cells. +-# Both `#%%`` and `# %%` are accepted, so `black` standardises +-# to the latter. +- +-# %% +-# %% +``` + +## Ruff Output + +```py +``` + +## Black Output + +```py +# The percent-percent comments are Spyder IDE cells. +# Both `#%%`` and `# %%` are accepted, so `black` standardises +# to the latter. + +# %% +# %% +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments9_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments9_py.snap index 53ba320c7370c..2e109bb331bf0 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments9_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments9_py.snap @@ -152,96 +152,115 @@ def bar(): ```diff --- Black +++ Ruff -@@ -35,9 +35,10 @@ +@@ -1,16 +1,12 @@ + # Test for https://github.com/psf/black/issues/246. + + some = statement +- +- + # This comment should be split from the statement above by two lines. + def function(): + pass some = statement -+ -+ - # This should be stick to the statement above +- +- + # This multiline comments section + # should be split from the statement + # above by two lines. +@@ -19,16 +15,12 @@ + + + some = statement +- +- + # This comment should be split from the statement above by two lines. + async def async_function(): + pass + + + some = statement +- +- + # This comment should be split from the statement above by two lines. + class MyClass: + pass +@@ -36,7 +28,6 @@ + some = statement + # This should be stick to the statement above - + # This should be split from the above by two lines class MyClassWithComplexLeadingComments: - pass -@@ -57,11 +58,11 @@ +@@ -45,16 +36,12 @@ - # leading 1 - @deco1 --# leading 2 -+@# leading 2 - # leading 2 extra --@deco2(with_args=True) --# leading 3 --@deco3 -+deco2(with_args=True) -+@# leading 3 -+deco3 - # leading 4 - def decorated(): + class ClassWithDocstring: + """A docstring.""" +- +- + # Leading comment after a class with just a docstring + class MyClassAfterAnotherClassWithDocstring: pass -@@ -72,11 +73,10 @@ + + some = statement +- +- # leading 1 @deco1 --# leading 2 --@deco2(with_args=True) -- --# leading 3 that already has an empty line --@deco3 -+@# leading 2 -+deco2(with_args=True) -+@# leading 3 that already has an empty line -+deco3 - # leading 4 - def decorated_with_split_leading_comments(): - pass -@@ -87,10 +87,10 @@ + # leading 2 +@@ -68,8 +55,6 @@ + + some = statement +- +- # leading 1 @deco1 --# leading 2 --@deco2(with_args=True) --# leading 3 --@deco3 -+@# leading 2 -+deco2(with_args=True) -+@# leading 3 -+deco3 + # leading 2 +@@ -83,8 +68,6 @@ - # leading 4 that already has an empty line - def decorated_with_split_leading_comments(): -@@ -99,6 +99,7 @@ - def main(): - if a: -+ + some = statement +- +- + # leading 1 + @deco1 + # leading 2 +@@ -102,11 +85,9 @@ # Leading comment before inline function def inline(): pass -@@ -108,12 +109,14 @@ +- + # Another leading comment + def another_inline(): pass - +- else: -+ # More leading comments def inline_after_else(): - pass - - - if a: -+ +@@ -117,11 +98,9 @@ # Leading comment before "top-level inline" function def top_level_quote_inline(): pass -@@ -123,6 +126,7 @@ +- + # Another leading comment + def another_top_level_quote_inline_inline(): pass - +- else: -+ # More leading comments def top_level_quote_inline_after_else(): - pass +@@ -153,7 +132,6 @@ + # Trailing comment that belongs to this function. + # NOTE this comment only has one empty line below, and the formatter + # should enforce two blank lines. +- + + @decorator1 + # A standalone comment ``` ## Ruff Output @@ -250,16 +269,12 @@ def bar(): # Test for https://github.com/psf/black/issues/246. some = statement - - # This comment should be split from the statement above by two lines. def function(): pass some = statement - - # This multiline comments section # should be split from the statement # above by two lines. @@ -268,24 +283,18 @@ def function(): some = statement - - # This comment should be split from the statement above by two lines. async def async_function(): pass some = statement - - # This comment should be split from the statement above by two lines. class MyClass: pass some = statement - - # This should be stick to the statement above # This should be split from the above by two lines @@ -295,51 +304,44 @@ class MyClassWithComplexLeadingComments: class ClassWithDocstring: """A docstring.""" - - # Leading comment after a class with just a docstring class MyClassAfterAnotherClassWithDocstring: pass some = statement - - # leading 1 @deco1 -@# leading 2 +# leading 2 # leading 2 extra -deco2(with_args=True) -@# leading 3 -deco3 +@deco2(with_args=True) +# leading 3 +@deco3 # leading 4 def decorated(): pass some = statement - - # leading 1 @deco1 -@# leading 2 -deco2(with_args=True) -@# leading 3 that already has an empty line -deco3 +# leading 2 +@deco2(with_args=True) + +# leading 3 that already has an empty line +@deco3 # leading 4 def decorated_with_split_leading_comments(): pass some = statement - - # leading 1 @deco1 -@# leading 2 -deco2(with_args=True) -@# leading 3 -deco3 +# leading 2 +@deco2(with_args=True) +# leading 3 +@deco3 # leading 4 that already has an empty line def decorated_with_split_leading_comments(): @@ -348,34 +350,26 @@ def decorated_with_split_leading_comments(): def main(): if a: - # Leading comment before inline function def inline(): pass - # Another leading comment def another_inline(): pass - else: - # More leading comments def inline_after_else(): pass if a: - # Leading comment before "top-level inline" function def top_level_quote_inline(): pass - # Another leading comment def another_top_level_quote_inline_inline(): pass - else: - # More leading comments def top_level_quote_inline_after_else(): pass @@ -407,7 +401,6 @@ def foo(): # NOTE this comment only has one empty line below, and the formatter # should enforce two blank lines. - @decorator1 # A standalone comment def bar(): diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments_non_breaking_space_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments_non_breaking_space_py.snap index 7c88e6c3a23aa..427a7d5d39f11 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments_non_breaking_space_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments_non_breaking_space_py.snap @@ -32,32 +32,34 @@ def function(a:int=42): ```diff --- Black +++ Ruff -@@ -1,23 +1,22 @@ - from .config import ( - ConfigTypeAttributes, - Int, +@@ -1,23 +1,19 @@ +-from .config import ( +- ConfigTypeAttributes, +- Int, - Path, # String, - # DEFAULT_TYPE_ATTRIBUTES, -+ Path, # String, ++from .config import ( ConfigTypeAttributes, Int, Path, # String, ++ # DEFAULT_TYPE_ATTRIBUTES, ) -result = 1 # A simple comment -result = (1,) # Another one +result = 1 # A simple comment -+result = (1,) # Another one ++result = ( 1, ) # Another one -result = 1 #  type: ignore -result = 1 # This comment is talking about type: ignore -square = Square(4) #  type: Optional[Square] -+result = 1 # type: ignore -+result = 1 # This comment is talking about type: ignore -+square = Square(4) # type: Optional[Square] ++result = 1 # type: ignore ++result = 1# This comment is talking about type: ignore ++square = Square(4) # type: Optional[Square] - - def function(a: int = 42): +- +-def function(a: int = 42): - """This docstring is already formatted - a - b ++def function(a:int=42): + """ This docstring is already formatted + a + b @@ -71,21 +73,18 @@ def function(a:int=42): ## Ruff Output ```py -from .config import ( - ConfigTypeAttributes, - Int, - Path, # String, +from .config import ( ConfigTypeAttributes, Int, Path, # String, + # DEFAULT_TYPE_ATTRIBUTES, ) result = 1 # A simple comment -result = (1,) # Another one - -result = 1 # type: ignore -result = 1 # This comment is talking about type: ignore -square = Square(4) # type: Optional[Square] +result = ( 1, ) # Another one +result = 1 # type: ignore +result = 1# This comment is talking about type: ignore +square = Square(4) # type: Optional[Square] -def function(a: int = 42): +def function(a:int=42): """ This docstring is already formatted a b diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__composition_no_trailing_comma_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__composition_no_trailing_comma_py.snap index b2ee4e01ad945..439ada09795d6 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__composition_no_trailing_comma_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__composition_no_trailing_comma_py.snap @@ -194,274 +194,86 @@ class C: ```diff --- Black +++ Ruff -@@ -2,7 +2,8 @@ - def test(self) -> None: - with patch("black.out", print): - self.assertEqual( -- unstyle(str(report)), "1 file reformatted, 1 file failed to reformat." -+ unstyle(str(report)), -+ "1 file reformatted, 1 file failed to reformat.", - ) - self.assertEqual( - unstyle(str(report)), -@@ -25,11 +26,8 @@ - # Rule 2 - and i % 3 == 0 - ): -- while ( -- # Just a comment -- call() -- # Another -- ): -+ while # Just a comment -+ call(): - print(i) +@@ -34,7 +34,7 @@ xxxxxxxxxxxxxxxx = Yyyy2YyyyyYyyyyy( push_manager=context.request.resource_manager, -@@ -39,116 +37,140 @@ + max_items_to_push=num_items, +- batch_size=Yyyy2YyyyYyyyyYyyy.FULL_SIZE, ++ batch_size=Yyyy2YyyyYyyyyYyyy.FULL_SIZE + ).push( # Only send the first n items. items=items[:num_items] - ) -- return ( -- 'Utterly failed doctest test for %s\n File "%s", line %s, in %s\n\n%s' -- % (test.name, test.filename, lineno, lname, err) -- ) -+ return 'Utterly failed doctest test for %s\n File "%s", line %s, in %s\n\n%s' -+ % (test.name, test.filename, lineno, lname, err) - - def omitting_trailers(self) -> None: - get_collection( -- hey_this_is_a_very_long_call, it_has_funny_attributes, really=True -+ hey_this_is_a_very_long_call, -+ it_has_funny_attributes, -+ really=True, - )[OneLevelIndex] - get_collection( -- hey_this_is_a_very_long_call, it_has_funny_attributes, really=True -+ hey_this_is_a_very_long_call, -+ it_has_funny_attributes, -+ really=True, - )[OneLevelIndex][TwoLevelIndex][ThreeLevelIndex][FourLevelIndex] - d[0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20][21][ - 22 - ] -- assignment = ( -- some.rather.elaborate.rule() and another.rule.ending_with.index[123] -- ) -+ assignment = some.rather.elaborate.rule() -+ and another.rule.ending_with.index[123] - - def easy_asserts(self) -> None: -- assert { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, +@@ -68,7 +68,7 @@ + key6: value6, + key7: value7, + key8: value8, - key9: value9, -- } == expected, "Not what we expected" -+ assert ( -+ { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ } -+ == expected -+ ), "Not what we expected" ++ key9: value9 + } == expected, "Not what we expected" -- assert expected == { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, + assert expected == { +@@ -80,7 +80,7 @@ + key6: value6, + key7: value7, + key8: value8, - key9: value9, -- }, "Not what we expected" -+ assert ( -+ expected -+ == { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ } -+ ), "Not what we expected" ++ key9: value9 + }, "Not what we expected" -- assert expected == { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, + assert expected == { +@@ -92,7 +92,7 @@ + key6: value6, + key7: value7, + key8: value8, - key9: value9, -- } -+ assert ( -+ expected -+ == { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ } -+ ) ++ key9: value9 + } def tricky_asserts(self) -> None: -- assert { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, +@@ -105,7 +105,7 @@ + key6: value6, + key7: value7, + key8: value8, - key9: value9, -- } == expected( -- value, is_going_to_be="too long to fit in a single line", srsly=True -+ assert ( -+ { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ } -+ == expected( -+ value, -+ is_going_to_be="too long to fit in a single line", -+ srsly=True, -+ ) ++ key9: value9 + } == expected( + value, is_going_to_be="too long to fit in a single line", srsly=True ), "Not what we expected" - -- assert { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, +@@ -119,7 +119,7 @@ + key6: value6, + key7: value7, + key8: value8, - key9: value9, -- } == expected, ( -- "Not what we expected and the message is too long to fit in one line" -- ) -+ assert ( -+ { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ } -+ == expected -+ ), "Not what we expected and the message is too long to fit in one line" - -- assert expected( -- value, is_going_to_be="too long to fit in a single line", srsly=True -- ) == { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, ++ key9: value9 + } == expected, ( + "Not what we expected and the message is too long to fit in one line" + ) +@@ -135,7 +135,7 @@ + key6: value6, + key7: value7, + key8: value8, - key9: value9, -- }, "Not what we expected" -+ assert ( -+ expected( -+ value, -+ is_going_to_be="too long to fit in a single line", -+ srsly=True, -+ ) -+ == { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ } -+ ), "Not what we expected" ++ key9: value9 + }, "Not what we expected" -- assert expected == { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, + assert expected == { +@@ -147,7 +147,7 @@ + key6: value6, + key7: value7, + key8: value8, - key9: value9, -- }, ( -+ assert ( -+ expected -+ == { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ } -+ ), ( ++ key9: value9 + }, ( "Not what we expected and the message is too long to fit in one line" " because it's too long" +@@ -176,6 +176,6 @@ + key6: value6, + key7: value7, + key8: value8, +- key9: value9, ++ key9: value9 + } ) -@@ -161,9 +183,8 @@ - 8 STORE_ATTR 0 (x) - 10 LOAD_CONST 0 (None) - 12 RETURN_VALUE -- """ % ( -- _C.__init__.__code__.co_firstlineno + 1, -- ) -+ """ -+ % (_C.__init__.__code__.co_firstlineno + 1,) - - assert ( - expectedexpectedexpectedexpectedexpectedexpectedexpectedexpectedexpect ``` ## Ruff Output @@ -471,8 +283,7 @@ class C: def test(self) -> None: with patch("black.out", print): self.assertEqual( - unstyle(str(report)), - "1 file reformatted, 1 file failed to reformat.", + unstyle(str(report)), "1 file reformatted, 1 file failed to reformat." ) self.assertEqual( unstyle(str(report)), @@ -495,151 +306,130 @@ class C: # Rule 2 and i % 3 == 0 ): - while # Just a comment - call(): + while ( + # Just a comment + call() + # Another + ): print(i) xxxxxxxxxxxxxxxx = Yyyy2YyyyyYyyyyy( push_manager=context.request.resource_manager, max_items_to_push=num_items, - batch_size=Yyyy2YyyyYyyyyYyyy.FULL_SIZE, + batch_size=Yyyy2YyyyYyyyyYyyy.FULL_SIZE ).push( # Only send the first n items. items=items[:num_items] ) - return 'Utterly failed doctest test for %s\n File "%s", line %s, in %s\n\n%s' - % (test.name, test.filename, lineno, lname, err) + return ( + 'Utterly failed doctest test for %s\n File "%s", line %s, in %s\n\n%s' + % (test.name, test.filename, lineno, lname, err) + ) def omitting_trailers(self) -> None: get_collection( - hey_this_is_a_very_long_call, - it_has_funny_attributes, - really=True, + hey_this_is_a_very_long_call, it_has_funny_attributes, really=True )[OneLevelIndex] get_collection( - hey_this_is_a_very_long_call, - it_has_funny_attributes, - really=True, + hey_this_is_a_very_long_call, it_has_funny_attributes, really=True )[OneLevelIndex][TwoLevelIndex][ThreeLevelIndex][FourLevelIndex] d[0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20][21][ 22 ] - assignment = some.rather.elaborate.rule() - and another.rule.ending_with.index[123] + assignment = ( + some.rather.elaborate.rule() and another.rule.ending_with.index[123] + ) def easy_asserts(self) -> None: - assert ( - { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - == expected - ), "Not what we expected" + assert { + key1: value1, + key2: value2, + key3: value3, + key4: value4, + key5: value5, + key6: value6, + key7: value7, + key8: value8, + key9: value9 + } == expected, "Not what we expected" - assert ( - expected - == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - ), "Not what we expected" + assert expected == { + key1: value1, + key2: value2, + key3: value3, + key4: value4, + key5: value5, + key6: value6, + key7: value7, + key8: value8, + key9: value9 + }, "Not what we expected" - assert ( - expected - == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - ) + assert expected == { + key1: value1, + key2: value2, + key3: value3, + key4: value4, + key5: value5, + key6: value6, + key7: value7, + key8: value8, + key9: value9 + } def tricky_asserts(self) -> None: - assert ( - { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - == expected( - value, - is_going_to_be="too long to fit in a single line", - srsly=True, - ) + assert { + key1: value1, + key2: value2, + key3: value3, + key4: value4, + key5: value5, + key6: value6, + key7: value7, + key8: value8, + key9: value9 + } == expected( + value, is_going_to_be="too long to fit in a single line", srsly=True ), "Not what we expected" - assert ( - { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - == expected - ), "Not what we expected and the message is too long to fit in one line" + assert { + key1: value1, + key2: value2, + key3: value3, + key4: value4, + key5: value5, + key6: value6, + key7: value7, + key8: value8, + key9: value9 + } == expected, ( + "Not what we expected and the message is too long to fit in one line" + ) - assert ( - expected( - value, - is_going_to_be="too long to fit in a single line", - srsly=True, - ) - == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - ), "Not what we expected" + assert expected( + value, is_going_to_be="too long to fit in a single line", srsly=True + ) == { + key1: value1, + key2: value2, + key3: value3, + key4: value4, + key5: value5, + key6: value6, + key7: value7, + key8: value8, + key9: value9 + }, "Not what we expected" - assert ( - expected - == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - ), ( + assert expected == { + key1: value1, + key2: value2, + key3: value3, + key4: value4, + key5: value5, + key6: value6, + key7: value7, + key8: value8, + key9: value9 + }, ( "Not what we expected and the message is too long to fit in one line" " because it's too long" ) @@ -652,8 +442,9 @@ class C: 8 STORE_ATTR 0 (x) 10 LOAD_CONST 0 (None) 12 RETURN_VALUE - """ - % (_C.__init__.__code__.co_firstlineno + 1,) + """ % ( + _C.__init__.__code__.co_firstlineno + 1, + ) assert ( expectedexpectedexpectedexpectedexpectedexpectedexpectedexpectedexpect @@ -666,7 +457,7 @@ class C: key6: value6, key7: value7, key8: value8, - key9: value9, + key9: value9 } ) ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__composition_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__composition_py.snap deleted file mode 100644 index ea63bb606d982..0000000000000 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__composition_py.snap +++ /dev/null @@ -1,860 +0,0 @@ ---- -source: crates/ruff_python_formatter/src/lib.rs -expression: snapshot -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/composition.py ---- -## Input - -```py -class C: - def test(self) -> None: - with patch("black.out", print): - self.assertEqual( - unstyle(str(report)), "1 file reformatted, 1 file failed to reformat." - ) - self.assertEqual( - unstyle(str(report)), - "1 file reformatted, 1 file left unchanged, 1 file failed to reformat.", - ) - self.assertEqual( - unstyle(str(report)), - "2 files reformatted, 1 file left unchanged, 1 file failed to" - " reformat.", - ) - self.assertEqual( - unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, 2 files failed to" - " reformat.", - ) - for i in (a,): - if ( - # Rule 1 - i % 2 == 0 - # Rule 2 - and i % 3 == 0 - ): - while ( - # Just a comment - call() - # Another - ): - print(i) - xxxxxxxxxxxxxxxx = Yyyy2YyyyyYyyyyy( - push_manager=context.request.resource_manager, - max_items_to_push=num_items, - batch_size=Yyyy2YyyyYyyyyYyyy.FULL_SIZE, - ).push( - # Only send the first n items. - items=items[:num_items] - ) - return ( - 'Utterly failed doctest test for %s\n File "%s", line %s, in %s\n\n%s' - % (test.name, test.filename, lineno, lname, err) - ) - - def omitting_trailers(self) -> None: - get_collection( - hey_this_is_a_very_long_call, it_has_funny_attributes, really=True - )[OneLevelIndex] - get_collection( - hey_this_is_a_very_long_call, it_has_funny_attributes, really=True - )[OneLevelIndex][TwoLevelIndex][ThreeLevelIndex][FourLevelIndex] - d[0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20][21][ - 22 - ] - assignment = ( - some.rather.elaborate.rule() and another.rule.ending_with.index[123] - ) - - def easy_asserts(self) -> None: - assert { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } == expected, "Not what we expected" - - assert expected == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - }, "Not what we expected" - - assert expected == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - - def tricky_asserts(self) -> None: - assert { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } == expected( - value, is_going_to_be="too long to fit in a single line", srsly=True - ), "Not what we expected" - - assert { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } == expected, ( - "Not what we expected and the message is too long to fit in one line" - ) - - assert expected( - value, is_going_to_be="too long to fit in a single line", srsly=True - ) == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - }, "Not what we expected" - - assert expected == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - }, ( - "Not what we expected and the message is too long to fit in one line" - " because it's too long" - ) - - dis_c_instance_method = """\ - %3d 0 LOAD_FAST 1 (x) - 2 LOAD_CONST 1 (1) - 4 COMPARE_OP 2 (==) - 6 LOAD_FAST 0 (self) - 8 STORE_ATTR 0 (x) - 10 LOAD_CONST 0 (None) - 12 RETURN_VALUE - """ % ( - _C.__init__.__code__.co_firstlineno + 1, - ) - - assert ( - expectedexpectedexpectedexpectedexpectedexpectedexpectedexpectedexpect - == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - ) -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -2,7 +2,8 @@ - def test(self) -> None: - with patch("black.out", print): - self.assertEqual( -- unstyle(str(report)), "1 file reformatted, 1 file failed to reformat." -+ unstyle(str(report)), -+ "1 file reformatted, 1 file failed to reformat.", - ) - self.assertEqual( - unstyle(str(report)), -@@ -25,11 +26,8 @@ - # Rule 2 - and i % 3 == 0 - ): -- while ( -- # Just a comment -- call() -- # Another -- ): -+ while # Just a comment -+ call(): - print(i) - xxxxxxxxxxxxxxxx = Yyyy2YyyyyYyyyyy( - push_manager=context.request.resource_manager, -@@ -39,116 +37,140 @@ - # Only send the first n items. - items=items[:num_items] - ) -- return ( -- 'Utterly failed doctest test for %s\n File "%s", line %s, in %s\n\n%s' -- % (test.name, test.filename, lineno, lname, err) -- ) -+ return 'Utterly failed doctest test for %s\n File "%s", line %s, in %s\n\n%s' -+ % (test.name, test.filename, lineno, lname, err) - - def omitting_trailers(self) -> None: - get_collection( -- hey_this_is_a_very_long_call, it_has_funny_attributes, really=True -+ hey_this_is_a_very_long_call, -+ it_has_funny_attributes, -+ really=True, - )[OneLevelIndex] - get_collection( -- hey_this_is_a_very_long_call, it_has_funny_attributes, really=True -+ hey_this_is_a_very_long_call, -+ it_has_funny_attributes, -+ really=True, - )[OneLevelIndex][TwoLevelIndex][ThreeLevelIndex][FourLevelIndex] - d[0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20][21][ - 22 - ] -- assignment = ( -- some.rather.elaborate.rule() and another.rule.ending_with.index[123] -- ) -+ assignment = some.rather.elaborate.rule() -+ and another.rule.ending_with.index[123] - - def easy_asserts(self) -> None: -- assert { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, -- key9: value9, -- } == expected, "Not what we expected" -+ assert ( -+ { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ } -+ == expected -+ ), "Not what we expected" - -- assert expected == { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, -- key9: value9, -- }, "Not what we expected" -+ assert ( -+ expected -+ == { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ } -+ ), "Not what we expected" - -- assert expected == { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, -- key9: value9, -- } -+ assert ( -+ expected -+ == { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ } -+ ) - - def tricky_asserts(self) -> None: -- assert { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, -- key9: value9, -- } == expected( -- value, is_going_to_be="too long to fit in a single line", srsly=True -+ assert ( -+ { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ } -+ == expected( -+ value, -+ is_going_to_be="too long to fit in a single line", -+ srsly=True, -+ ) - ), "Not what we expected" - -- assert { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, -- key9: value9, -- } == expected, ( -- "Not what we expected and the message is too long to fit in one line" -- ) -+ assert ( -+ { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ } -+ == expected -+ ), "Not what we expected and the message is too long to fit in one line" - -- assert expected( -- value, is_going_to_be="too long to fit in a single line", srsly=True -- ) == { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, -- key9: value9, -- }, "Not what we expected" -+ assert ( -+ expected( -+ value, -+ is_going_to_be="too long to fit in a single line", -+ srsly=True, -+ ) -+ == { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ } -+ ), "Not what we expected" - -- assert expected == { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, -- key9: value9, -- }, ( -+ assert ( -+ expected -+ == { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ } -+ ), ( - "Not what we expected and the message is too long to fit in one line" - " because it's too long" - ) -@@ -161,9 +183,8 @@ - 8 STORE_ATTR 0 (x) - 10 LOAD_CONST 0 (None) - 12 RETURN_VALUE -- """ % ( -- _C.__init__.__code__.co_firstlineno + 1, -- ) -+ """ -+ % (_C.__init__.__code__.co_firstlineno + 1,) - - assert ( - expectedexpectedexpectedexpectedexpectedexpectedexpectedexpectedexpect -``` - -## Ruff Output - -```py -class C: - def test(self) -> None: - with patch("black.out", print): - self.assertEqual( - unstyle(str(report)), - "1 file reformatted, 1 file failed to reformat.", - ) - self.assertEqual( - unstyle(str(report)), - "1 file reformatted, 1 file left unchanged, 1 file failed to reformat.", - ) - self.assertEqual( - unstyle(str(report)), - "2 files reformatted, 1 file left unchanged, 1 file failed to" - " reformat.", - ) - self.assertEqual( - unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, 2 files failed to" - " reformat.", - ) - for i in (a,): - if ( - # Rule 1 - i % 2 == 0 - # Rule 2 - and i % 3 == 0 - ): - while # Just a comment - call(): - print(i) - xxxxxxxxxxxxxxxx = Yyyy2YyyyyYyyyyy( - push_manager=context.request.resource_manager, - max_items_to_push=num_items, - batch_size=Yyyy2YyyyYyyyyYyyy.FULL_SIZE, - ).push( - # Only send the first n items. - items=items[:num_items] - ) - return 'Utterly failed doctest test for %s\n File "%s", line %s, in %s\n\n%s' - % (test.name, test.filename, lineno, lname, err) - - def omitting_trailers(self) -> None: - get_collection( - hey_this_is_a_very_long_call, - it_has_funny_attributes, - really=True, - )[OneLevelIndex] - get_collection( - hey_this_is_a_very_long_call, - it_has_funny_attributes, - really=True, - )[OneLevelIndex][TwoLevelIndex][ThreeLevelIndex][FourLevelIndex] - d[0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20][21][ - 22 - ] - assignment = some.rather.elaborate.rule() - and another.rule.ending_with.index[123] - - def easy_asserts(self) -> None: - assert ( - { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - == expected - ), "Not what we expected" - - assert ( - expected - == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - ), "Not what we expected" - - assert ( - expected - == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - ) - - def tricky_asserts(self) -> None: - assert ( - { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - == expected( - value, - is_going_to_be="too long to fit in a single line", - srsly=True, - ) - ), "Not what we expected" - - assert ( - { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - == expected - ), "Not what we expected and the message is too long to fit in one line" - - assert ( - expected( - value, - is_going_to_be="too long to fit in a single line", - srsly=True, - ) - == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - ), "Not what we expected" - - assert ( - expected - == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - ), ( - "Not what we expected and the message is too long to fit in one line" - " because it's too long" - ) - - dis_c_instance_method = """\ - %3d 0 LOAD_FAST 1 (x) - 2 LOAD_CONST 1 (1) - 4 COMPARE_OP 2 (==) - 6 LOAD_FAST 0 (self) - 8 STORE_ATTR 0 (x) - 10 LOAD_CONST 0 (None) - 12 RETURN_VALUE - """ - % (_C.__init__.__code__.co_firstlineno + 1,) - - assert ( - expectedexpectedexpectedexpectedexpectedexpectedexpectedexpectedexpect - == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - ) -``` - -## Black Output - -```py -class C: - def test(self) -> None: - with patch("black.out", print): - self.assertEqual( - unstyle(str(report)), "1 file reformatted, 1 file failed to reformat." - ) - self.assertEqual( - unstyle(str(report)), - "1 file reformatted, 1 file left unchanged, 1 file failed to reformat.", - ) - self.assertEqual( - unstyle(str(report)), - "2 files reformatted, 1 file left unchanged, 1 file failed to" - " reformat.", - ) - self.assertEqual( - unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, 2 files failed to" - " reformat.", - ) - for i in (a,): - if ( - # Rule 1 - i % 2 == 0 - # Rule 2 - and i % 3 == 0 - ): - while ( - # Just a comment - call() - # Another - ): - print(i) - xxxxxxxxxxxxxxxx = Yyyy2YyyyyYyyyyy( - push_manager=context.request.resource_manager, - max_items_to_push=num_items, - batch_size=Yyyy2YyyyYyyyyYyyy.FULL_SIZE, - ).push( - # Only send the first n items. - items=items[:num_items] - ) - return ( - 'Utterly failed doctest test for %s\n File "%s", line %s, in %s\n\n%s' - % (test.name, test.filename, lineno, lname, err) - ) - - def omitting_trailers(self) -> None: - get_collection( - hey_this_is_a_very_long_call, it_has_funny_attributes, really=True - )[OneLevelIndex] - get_collection( - hey_this_is_a_very_long_call, it_has_funny_attributes, really=True - )[OneLevelIndex][TwoLevelIndex][ThreeLevelIndex][FourLevelIndex] - d[0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20][21][ - 22 - ] - assignment = ( - some.rather.elaborate.rule() and another.rule.ending_with.index[123] - ) - - def easy_asserts(self) -> None: - assert { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } == expected, "Not what we expected" - - assert expected == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - }, "Not what we expected" - - assert expected == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - - def tricky_asserts(self) -> None: - assert { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } == expected( - value, is_going_to_be="too long to fit in a single line", srsly=True - ), "Not what we expected" - - assert { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } == expected, ( - "Not what we expected and the message is too long to fit in one line" - ) - - assert expected( - value, is_going_to_be="too long to fit in a single line", srsly=True - ) == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - }, "Not what we expected" - - assert expected == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - }, ( - "Not what we expected and the message is too long to fit in one line" - " because it's too long" - ) - - dis_c_instance_method = """\ - %3d 0 LOAD_FAST 1 (x) - 2 LOAD_CONST 1 (1) - 4 COMPARE_OP 2 (==) - 6 LOAD_FAST 0 (self) - 8 STORE_ATTR 0 (x) - 10 LOAD_CONST 0 (None) - 12 RETURN_VALUE - """ % ( - _C.__init__.__code__.co_firstlineno + 1, - ) - - assert ( - expectedexpectedexpectedexpectedexpectedexpectedexpectedexpectedexpect - == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - ) -``` - - diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_preview_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_preview_py.snap index fed3568a34020..b95e94cf32ef0 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_preview_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_preview_py.snap @@ -63,7 +63,7 @@ def single_quote_docstring_over_line_limit2(): ```diff --- Black +++ Ruff -@@ -1,9 +1,13 @@ +@@ -1,9 +1,11 @@ def docstring_almost_at_line_limit(): - """long docstring.................................................................""" + """long docstring................................................................. @@ -72,40 +72,17 @@ def single_quote_docstring_over_line_limit2(): def docstring_almost_at_line_limit_with_prefix(): - f"""long docstring................................................................""" -+ ( -+ f"""long docstring................................................................ ++ f"""long docstring................................................................ + """ -+ ) def mulitline_docstring_almost_at_line_limit(): -@@ -14,10 +18,12 @@ +@@ -45,4 +47,4 @@ - def mulitline_docstring_almost_at_line_limit_with_prefix(): -- f"""long docstring................................................................ -+ ( -+ f"""long docstring................................................................ - - .................................................................................. - """ -+ ) - - - def docstring_at_line_limit(): -@@ -35,9 +41,11 @@ - - - def multiline_docstring_at_line_limit_with_prefix(): -- f"""first line---------------------------------------------------------------------- -+ ( -+ f"""first line---------------------------------------------------------------------- - - second line----------------------------------------------------------------------""" -+ ) - - - def single_quote_docstring_over_line_limit(): + def single_quote_docstring_over_line_limit2(): +- "We do not want to put the closing quote on a new line as that is invalid (see GH-3141)." ++ 'We do not want to put the closing quote on a new line as that is invalid (see GH-3141).' ``` ## Ruff Output @@ -117,10 +94,8 @@ def docstring_almost_at_line_limit(): def docstring_almost_at_line_limit_with_prefix(): - ( - f"""long docstring................................................................ + f"""long docstring................................................................ """ - ) def mulitline_docstring_almost_at_line_limit(): @@ -131,12 +106,10 @@ def mulitline_docstring_almost_at_line_limit(): def mulitline_docstring_almost_at_line_limit_with_prefix(): - ( - f"""long docstring................................................................ + f"""long docstring................................................................ .................................................................................. """ - ) def docstring_at_line_limit(): @@ -154,11 +127,9 @@ def multiline_docstring_at_line_limit(): def multiline_docstring_at_line_limit_with_prefix(): - ( - f"""first line---------------------------------------------------------------------- + f"""first line---------------------------------------------------------------------- second line----------------------------------------------------------------------""" - ) def single_quote_docstring_over_line_limit(): @@ -166,7 +137,7 @@ def single_quote_docstring_over_line_limit(): def single_quote_docstring_over_line_limit2(): - "We do not want to put the closing quote on a new line as that is invalid (see GH-3141)." + 'We do not want to put the closing quote on a new line as that is invalid (see GH-3141).' ``` ## Black Output diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_py.snap index da177f03533c5..211c0527f1220 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__docstring_py.snap @@ -234,73 +234,85 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): ```diff --- Black +++ Ruff -@@ -1,83 +1,85 @@ +@@ -1,83 +1,84 @@ class MyClass: -- """Multiline -- class docstring -- """ -+ """ Multiline ++ """ Multiline + class docstring + """ - - def method(self): - """Multiline ++ ++ def method(self): + """Multiline +- class docstring ++ method docstring + """ +- +- def method(self): +- """Multiline - method docstring - """ -+ method docstring -+ """ - pass +- pass ++ pass def foo(): - """This is a docstring with - some lines of text here - """ -+ """This is a docstring with +- return ++ """This is a docstring with + some lines of text here + """ - return ++ return def bar(): - """This is another docstring +- """This is another docstring - with more lines of text - """ +- return ++ '''This is another docstring + with more lines of text -+ """ - return ++ ''' ++ return def baz(): - '''"This" is a string with some +- '''"This" is a string with some - embedded "quotes"''' +- return ++ '''"This" is a string with some + embedded "quotes"''' - return ++ return def troz(): - """Indentation with tabs +- """Indentation with tabs - is just as OK - """ +- return ++ '''Indentation with tabs + is just as OK -+ """ - return ++ ''' ++ return def zort(): - """Another +- """Another - multiline - docstring - """ +- pass +- ++ """Another + multiline + docstring + """ - pass - ++ pass def poit(): - """ +- """ - Lorem ipsum dolor sit amet. ++ """ + Lorem ipsum dolor sit amet. - Consectetur adipiscing elit: @@ -310,6 +322,7 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): - - quis nostrud exercitation ullamco laboris nisi - - aliquip ex ea commodo consequat - """ +- pass + Consectetur adipiscing elit: + - sed do eiusmod tempor incididunt ut labore + - dolore magna aliqua @@ -317,30 +330,34 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): + - quis nostrud exercitation ullamco laboris nisi + - aliquip ex ea commodo consequat + """ - pass ++ pass def under_indent(): - """ +- """ - These lines are indented in a way that does not - make sense. - """ +- pass ++ """ + These lines are indented in a way that does not +make sense. + """ - pass ++ pass def over_indent(): - """ -- This has a shallow indent -- - But some lines are deeper -- - And the closing quote is too deep ++ """ + This has a shallow indent + - But some lines are deeper + - And the closing quote is too deep """ - pass +- This has a shallow indent +- - But some lines are deeper +- - And the closing quote is too deep +- """ +- pass ++ pass def single_line(): @@ -351,56 +368,70 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): pass -@@ -93,20 +95,25 @@ +@@ -88,25 +89,30 @@ + + + def that(): +- """ "hey yah" """ ++ """ "hey yah" """ + def and_that(): - """ +- """ - "hey yah" """ ++ """ + "hey yah" """ def and_this(): - ''' - "hey yah"''' -+ ''' ++ ''' + "hey yah"''' def multiline_whitespace(): - """ """ -+ """ ++ ''' + + + + -+ """ ++ ''' def oneline_whitespace(): - """ """ -+ """ """ ++ ''' ''' def empty(): -@@ -118,8 +125,8 @@ +@@ -114,46 +120,43 @@ + + def single_quotes(): +- "testing" ++ 'testing' - def believe_it_or_not_this_is_in_the_py_stdlib(): + +-def believe_it_or_not_this_is_in_the_py_stdlib(): - ''' - "hey yah"''' -+ ''' ++def believe_it_or_not_this_is_in_the_py_stdlib(): ''' +"hey yah"''' def ignored_docstring(): -@@ -128,31 +135,31 @@ - + """a => \ +-b""" +- ++b""" def single_line_docstring_with_whitespace(): - """This should be stripped""" +- + """ This should be stripped """ - def docstring_with_inline_tabs_and_space_indentation(): """hey @@ -419,7 +450,8 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): def docstring_with_inline_tabs_and_tab_indentation(): - """hey +- """hey ++ """hey - tab separated value - tab at start of line and then a tab separated value @@ -428,6 +460,7 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): - - line ends with some tabs - """ +- pass + tab separated value + tab at start of line and then a tab separated value + multiple tabs at the beginning and inline @@ -435,28 +468,43 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): + + line ends with some tabs + """ - pass ++ pass -@@ -168,7 +175,7 @@ + def backslash_space(): +@@ -161,16 +164,15 @@ + + + def multiline_backslash_1(): +- """ ++ ''' + hey\there\ +- \ """ ++ \ ''' + def multiline_backslash_2(): - """ +- """ - hey there \ """ -+ hey there \ """ - ++ ''' ++ hey there \ ''' +- # Regression test for #3425 -@@ -179,7 +186,7 @@ + def multiline_backslash_really_long_dont_crash(): + """ +@@ -178,8 +180,8 @@ + def multiline_backslash_3(): - """ +- """ - already escaped \\""" -+ already escaped \\ """ ++ ''' ++ already escaped \\ ''' def my_god_its_full_of_stars_1(): -@@ -188,7 +195,7 @@ +@@ -188,7 +190,7 @@ # the space below is actually a \u2001, removed in output def my_god_its_full_of_stars_2(): @@ -465,60 +513,69 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): def docstring_almost_at_line_limit(): +@@ -213,7 +215,7 @@ + + + def stable_quote_normalization_with_immediate_inner_single_quote(self): +- """' ++ '''' + + +- """ ++ ''' ``` ## Ruff Output ```py class MyClass: - """ Multiline + """ Multiline class docstring """ - def method(self): - """Multiline + def method(self): + """Multiline method docstring """ - pass + pass def foo(): - """This is a docstring with + """This is a docstring with some lines of text here """ - return + return def bar(): - """This is another docstring + '''This is another docstring with more lines of text - """ - return + ''' + return def baz(): - '''"This" is a string with some + '''"This" is a string with some embedded "quotes"''' - return + return def troz(): - """Indentation with tabs + '''Indentation with tabs is just as OK - """ - return + ''' + return def zort(): - """Another + """Another multiline docstring """ - pass - + pass def poit(): - """ + """ Lorem ipsum dolor sit amet. Consectetur adipiscing elit: @@ -528,24 +585,24 @@ def poit(): - quis nostrud exercitation ullamco laboris nisi - aliquip ex ea commodo consequat """ - pass + pass def under_indent(): - """ + """ These lines are indented in a way that does not make sense. """ - pass + pass def over_indent(): - """ + """ This has a shallow indent - But some lines are deeper - And the closing quote is too deep """ - pass + pass def single_line(): @@ -562,30 +619,30 @@ def this(): def that(): - """ "hey yah" """ + """ "hey yah" """ def and_that(): - """ + """ "hey yah" """ def and_this(): - ''' + ''' "hey yah"''' def multiline_whitespace(): - """ + ''' - """ + ''' def oneline_whitespace(): - """ """ + ''' ''' def empty(): @@ -593,23 +650,20 @@ def empty(): def single_quotes(): - "testing" + 'testing' -def believe_it_or_not_this_is_in_the_py_stdlib(): - ''' +def believe_it_or_not_this_is_in_the_py_stdlib(): ''' "hey yah"''' def ignored_docstring(): """a => \ -b""" - +b""" def single_line_docstring_with_whitespace(): """ This should be stripped """ - def docstring_with_inline_tabs_and_space_indentation(): """hey @@ -623,7 +677,7 @@ def docstring_with_inline_tabs_and_space_indentation(): def docstring_with_inline_tabs_and_tab_indentation(): - """hey + """hey tab separated value tab at start of line and then a tab separated value @@ -632,7 +686,7 @@ def docstring_with_inline_tabs_and_tab_indentation(): line ends with some tabs """ - pass + pass def backslash_space(): @@ -640,15 +694,14 @@ def backslash_space(): def multiline_backslash_1(): - """ + ''' hey\there\ - \ """ + \ ''' def multiline_backslash_2(): - """ - hey there \ """ - + ''' + hey there \ ''' # Regression test for #3425 def multiline_backslash_really_long_dont_crash(): @@ -657,8 +710,8 @@ def multiline_backslash_really_long_dont_crash(): def multiline_backslash_3(): - """ - already escaped \\ """ + ''' + already escaped \\ ''' def my_god_its_full_of_stars_1(): @@ -692,10 +745,10 @@ def multiline_docstring_at_line_limit(): def stable_quote_normalization_with_immediate_inner_single_quote(self): - """' + '''' - """ + ''' ``` ## Black Output diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__empty_lines_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__empty_lines_py.snap index e8442851d3832..7e14fea9d24f7 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__empty_lines_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__empty_lines_py.snap @@ -105,61 +105,60 @@ def g(): ```diff --- Black +++ Ruff -@@ -25,23 +25,30 @@ +@@ -3,9 +3,9 @@ + + # leading comment + def f(): +- NO = "" +- SPACE = " " +- DOUBLESPACE = " " ++ NO = '' ++ SPACE = ' ' ++ DOUBLESPACE = ' ' + + t = leaf.type + p = leaf.parent # trailing comment +@@ -16,14 +16,19 @@ + if t == token.COMMENT: # another trailing comment + return DOUBLESPACE + ++ + assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}" + ++ + prev = leaf.prev_sibling + if not prev: + prevp = preceding_leaf(p) + if not prevp or prevp.type in OPENING_BRACKETS: ++ ++ return NO ++ if prevp.type == token.EQUAL: -- if prevp.parent and prevp.parent.type in { -- syms.typedargslist, -- syms.varargslist, -- syms.parameters, -- syms.arglist, -- syms.argument, -- }: -+ if ( -+ prevp.parent -+ and prevp.parent.type -+ in { -+ syms.typedargslist, -+ syms.varargslist, -+ syms.parameters, -+ syms.arglist, -+ syms.argument, -+ } -+ ): + if prevp.parent and prevp.parent.type in { + syms.typedargslist, +@@ -44,16 +49,14 @@ + }: return NO -- - elif prevp.type == token.DOUBLESTAR: -- if prevp.parent and prevp.parent.type in { -- syms.typedargslist, -- syms.varargslist, -- syms.parameters, -- syms.arglist, -- syms.dictsetmaker, -- }: -+ if ( -+ prevp.parent -+ and prevp.parent.type -+ in { -+ syms.typedargslist, -+ syms.varargslist, -+ syms.parameters, -+ syms.arglist, -+ syms.dictsetmaker, -+ } -+ ): - return NO - -@@ -49,7 +56,6 @@ +- + ############################################################################### # SECTION BECAUSE SECTIONS ############################################################################### - - + def g(): - NO = "" - SPACE = " " -@@ -67,7 +73,7 @@ +- NO = "" +- SPACE = " " +- DOUBLESPACE = " " ++ NO = '' ++ SPACE = ' ' ++ DOUBLESPACE = ' ' + + t = leaf.type + p = leaf.parent +@@ -67,7 +70,7 @@ return DOUBLESPACE # Another comment because more comments @@ -168,29 +167,6 @@ def g(): prev = leaf.prev_sibling if not prev: -@@ -79,11 +85,15 @@ - return NO - - if prevp.type == token.EQUAL: -- if prevp.parent and prevp.parent.type in { -- syms.typedargslist, -- syms.varargslist, -- syms.parameters, -- syms.arglist, -- syms.argument, -- }: -+ if ( -+ prevp.parent -+ and prevp.parent.type -+ in { -+ syms.typedargslist, -+ syms.varargslist, -+ syms.parameters, -+ syms.arglist, -+ syms.argument, -+ } -+ ): - return NO ``` ## Ruff Output @@ -201,9 +177,9 @@ def g(): # leading comment def f(): - NO = "" - SPACE = " " - DOUBLESPACE = " " + NO = '' + SPACE = ' ' + DOUBLESPACE = ' ' t = leaf.type p = leaf.parent # trailing comment @@ -214,50 +190,47 @@ def f(): if t == token.COMMENT: # another trailing comment return DOUBLESPACE + assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}" + prev = leaf.prev_sibling if not prev: prevp = preceding_leaf(p) if not prevp or prevp.type in OPENING_BRACKETS: + + return NO + if prevp.type == token.EQUAL: - if ( - prevp.parent - and prevp.parent.type - in { - syms.typedargslist, - syms.varargslist, - syms.parameters, - syms.arglist, - syms.argument, - } - ): + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.argument, + }: return NO + elif prevp.type == token.DOUBLESTAR: - if ( - prevp.parent - and prevp.parent.type - in { - syms.typedargslist, - syms.varargslist, - syms.parameters, - syms.arglist, - syms.dictsetmaker, - } - ): + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.dictsetmaker, + }: return NO - ############################################################################### # SECTION BECAUSE SECTIONS ############################################################################### def g(): - NO = "" - SPACE = " " - DOUBLESPACE = " " + NO = '' + SPACE = ' ' + DOUBLESPACE = ' ' t = leaf.type p = leaf.parent @@ -283,17 +256,13 @@ def g(): return NO if prevp.type == token.EQUAL: - if ( - prevp.parent - and prevp.parent.type - in { - syms.typedargslist, - syms.varargslist, - syms.parameters, - syms.arglist, - syms.argument, - } - ): + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.argument, + }: return NO ``` 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 adb746d4f8581..e6606c57c836e 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 @@ -267,55 +267,215 @@ last_call() ```diff --- Black +++ Ruff -@@ -1,3 +1,4 @@ +@@ -1,5 +1,6 @@ +-"some_string" +-b"\\xa3" +... - "some_string" - b"\\xa3" ++'some_string' ++b'\\xa3' Name -@@ -38,7 +39,8 @@ - lambda a, b, c=True, *, d=(1 << v2), e="str": a - lambda a, b, c=True, *vararg, d=(v1 << 2), e="str", **kwargs: a + b + 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 + -1 + ~int and not v1 ^ 123 + v2 | True + (~int) and (not ((v1 ^ (123 + v2)) | True)) +-+(really ** -(confusing ** ~(operator**-precedence))) +-flags & ~select.EPOLLIN and waiters.write_task is not None +++really ** -confusing ** ~operator ** -precedence ++flags & ~ select.EPOLLIN and waiters.write_task is not None + lambda arg: None + lambda a=True: a + lambda a, b, c=True: a +-lambda a, b, c=True, *, d=(1 << v2), e="str": a +-lambda a, b, c=True, *vararg, d=(v1 << 2), e="str", **kwargs: a + b ++lambda a, b, c=True, *, d=(1 << v2), e='str': a ++lambda a, b, c=True, *vararg, d=(v1 << 2), e='str', **kwargs: a + b manylambdas = lambda x=lambda y=lambda z=1: z: y(): x() -foo = lambda port_id, ignore_missing: { -+foo = lambda port_id, -+ignore_missing,: { - "port1": port1_resource, - "port2": port2_resource, - }[port_id] -@@ -100,7 +102,8 @@ +- "port1": port1_resource, +- "port2": port2_resource, +-}[port_id] ++foo = (lambda port_id, ignore_missing: {"port1": port1_resource, "port2": port2_resource}[port_id]) + 1 if True else 2 + 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) +-) +-{"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} +-{"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 ++{'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 + () + (1,) + (1, 2) + (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"))} +-{((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)} ++{(i ** 2) for i in (1, 2, 3)} ++{(i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))} ++{((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)} + [i for i in (1, 2, 3)] +-[(i**2) for i in (1, 2, 3)] +-[(i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))] +-[((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)] ++[(i ** 2) for i in (1, 2, 3)] ++[(i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))] ++[((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)] + {i: 0 for i in (1, 2, 3)} +-{i: j for i, j in ((1, "a"), (2, "b"), (3, "c"))} ++{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 +-{ +- k: v - for k, v in this_is_a_very_long_variable_which_will_cause_a_trailing_comma_which_breaks_the_comprehension -+ for k, -+ v, in this_is_a_very_long_variable_which_will_cause_a_trailing_comma_which_breaks_the_comprehension - } +-} ++{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 -@@ -138,15 +141,15 @@ + 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(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 ++1 .real ++1.0 .real + ....__class__ + 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] + ] ++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]]]], ] --xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore -- sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__) -+xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = ( -+ classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) # type: ignore - ) --xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore -- sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__) -+xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = ( -+ classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) # type: ignore -+) -+xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = ( -+ classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) +@@ -144,9 +109,9 @@ + xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore + sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__) ) -xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( - sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__) -) # type: ignore ++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] -@@ -201,30 +204,26 @@ +@@ -174,57 +139,29 @@ + 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"} ++{'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 + (Good, Bad, Ugly) + (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"))) +-(((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)) ++((i ** 2) for i in (1, 2, 3)) ++((i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))) ++(((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)) + (*starred,) +-{ +- "id": "1", +- "type": "type", +- "started_at": now(), +- "ended_at": now() + timedelta(days=10), +- "priority": 1, +- "import_session_id": 1, +- **kwargs, +-} ++{"id": "1","type": "type","started_at": now(),"ended_at": now() + timedelta(days=10),"priority": 1,"import_session_id": 1,**kwargs} + a = (1,) +-b = (1,) ++b = 1, + c = 1 + d = (1,) + a + (2,) e = (1,).count(1) f = 1, *range(10) g = 1, *"ten" @@ -332,11 +492,7 @@ last_call() - ) - .order_by(models.Customer.id.asc()) - .all() -+what_is_up_with_those_new_coord_names = ( -+ (coord_names -+ + set(vars_to_create)) -+ + set(vars_to_remove) - ) +-) -result = ( - session.query(models.Customer.id) - .filter( @@ -346,77 +502,195 @@ last_call() - models.Customer.id.asc(), - ) - .all() -+what_is_up_with_those_new_coord_names = ( -+ (coord_names -+ | set(vars_to_create)) -+ - set(vars_to_remove) - ) -+result = session.query(models.Customer.id).filter( -+ models.Customer.account_id == account_id, -+ models.Customer.email == email_address, -+).order_by(models.Customer.id.asc()).all() -+result = session.query(models.Customer.id).filter( -+ models.Customer.account_id == account_id, -+ models.Customer.email == email_address, -+).order_by( -+ models.Customer.id.asc(), -+).all() +-) ++what_is_up_with_those_new_coord_names = (coord_names + set(vars_to_create)) + set(vars_to_remove) ++what_is_up_with_those_new_coord_names = (coord_names | set(vars_to_create)) - set(vars_to_remove) ++result = session.query(models.Customer.id).filter(models.Customer.account_id == account_id, models.Customer.email == email_address).order_by(models.Customer.id.asc()).all() ++result = session.query(models.Customer.id).filter(models.Customer.account_id == account_id, models.Customer.email == email_address).order_by(models.Customer.id.asc(),).all() Ø = set() authors.łukasz.say_thanks() mapping = { -@@ -250,9 +249,9 @@ +@@ -233,138 +170,84 @@ + C: 0.1 * (10.0 / 12), + D: 0.1 * (10.0 / 12), + } +- + + def gen(): + yield from outside_of_generator +- a = yield +- b = yield +- c = yield +- ++ a = (yield) ++ b = ((yield)) ++ c = (((yield))) + + async def f(): + await some.complicated[0].call(with_args=(True or (1 is not 1))) +- +- +-print(*[] or [1]) ++print(* [] or [1]) print(**{1: 3} if False else {x: x for x in range(3)}) - print(*lambda x: x) - assert not Test, "Short message" +-print(*lambda x: x) +-assert not Test, "Short message" -assert this is ComplexTest and not requirements.fit_in_a_single_line( - force=False -), "Short message" -+assert this is ComplexTest and not requirements.fit_in_a_single_line(force=False), ( -+ "Short message" -+) - assert parens is TooMany - for (x,) in (1,), (2,), (3,): - ... -@@ -327,13 +326,18 @@ +-assert parens is TooMany +-for (x,) in (1,), (2,), (3,): +- ... +-for y in (): +- ... +-for z in (i for i in (1, 2, 3)): +- ... +-for i in call(): +- ... +-for j in 1 + (2 + 3): +- ... +-while this and that: +- ... +-for ( +- addr_family, +- addr_type, +- addr_proto, +- addr_canonname, +- addr_sockaddr, +-) in socket.getaddrinfo("google.com", "http"): ++print(* lambda x: x) ++assert(not Test),("Short message") ++assert this is ComplexTest and not requirements.fit_in_a_single_line(force=False), "Short message" ++assert(((parens is TooMany))) ++for x, in (1,), (2,), (3,): ... ++for y in (): ... ++for z in (i for i in (1, 2, 3)): ... ++for i in (call()): ... ++for j in (1 + (2 + 3)): ... ++while(this and that): ... ++for addr_family, addr_type, addr_proto, addr_canonname, addr_sockaddr in socket.getaddrinfo('google.com', 'http'): + pass +-a = ( +- aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp +- in qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz +-) +-a = ( +- aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp +- not in qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz +-) +-a = ( +- aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp +- is qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz +-) +-a = ( +- aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp +- is not qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz +-) ++a = aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp in qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz ++a = aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp not in qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz ++a = aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp is qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz ++a = aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp is not qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz + if ( +- threading.current_thread() != threading.main_thread() +- and threading.current_thread() != threading.main_thread() +- or signal.getsignal(signal.SIGINT) != signal.default_int_handler ++ threading.current_thread() != threading.main_thread() and ++ threading.current_thread() != threading.main_thread() or ++ signal.getsignal(signal.SIGINT) != signal.default_int_handler + ): + return True + if ( ++ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +- | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ): + return True + if ( ++ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa & + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +- & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ): + return True + if ( ++ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +- + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ): + return True + if ( ++ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +- - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ): + return True + if ( ++ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa * + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +- * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ): + return True + if ( ++ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa / + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +- / aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ): return True if ( - ~aaaa.a + aaaa.b - aaaa.c * aaaa.d / aaaa.e -+ ~aaaa.a -+ + aaaa.b -+ - aaaa.c * aaaa.d / aaaa.e - | aaaa.f & aaaa.g % aaaa.h ^ aaaa.i << aaaa.k >> aaaa.l**aaaa.m // aaaa.n +- | aaaa.f & aaaa.g % aaaa.h ^ aaaa.i << aaaa.k >> aaaa.l**aaaa.m // aaaa.n ++ ~ aaaa.a + aaaa.b - aaaa.c * aaaa.d / aaaa.e | aaaa.f & aaaa.g % aaaa.h ^ aaaa.i << aaaa.k >> aaaa.l ** aaaa.m // aaaa.n ): return True if ( - ~aaaaaaaa.a + aaaaaaaa.b - aaaaaaaa.c @ aaaaaaaa.d / aaaaaaaa.e - | aaaaaaaa.f & aaaaaaaa.g % aaaaaaaa.h -+ ~aaaaaaaa.a -+ + aaaaaaaa.b -+ - aaaaaaaa.c @ aaaaaaaa.d / aaaaaaaa.e -+ | aaaaaaaa.f -+ & aaaaaaaa.g % aaaaaaaa.h - ^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l**aaaaaaaa.m // aaaaaaaa.n +- ^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l**aaaaaaaa.m // aaaaaaaa.n ++ ~ aaaaaaaa.a + aaaaaaaa.b - aaaaaaaa.c @ aaaaaaaa.d / aaaaaaaa.e | aaaaaaaa.f & aaaaaaaa.g % aaaaaaaa.h ^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l ** aaaaaaaa.m // aaaaaaaa.n ): return True -@@ -341,7 +345,8 @@ - ~aaaaaaaaaaaaaaaa.a - + aaaaaaaaaaaaaaaa.b - - aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e + if ( +- ~aaaaaaaaaaaaaaaa.a +- + aaaaaaaaaaaaaaaa.b +- - aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e - | aaaaaaaaaaaaaaaa.f & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h -+ | aaaaaaaaaaaaaaaa.f -+ & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h - ^ aaaaaaaaaaaaaaaa.i - << aaaaaaaaaaaaaaaa.k - >> aaaaaaaaaaaaaaaa.l**aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n +- ^ aaaaaaaaaaaaaaaa.i +- << aaaaaaaaaaaaaaaa.k +- >> aaaaaaaaaaaaaaaa.l**aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n ++ ~ aaaaaaaaaaaaaaaa.a + aaaaaaaaaaaaaaaa.b - aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e | aaaaaaaaaaaaaaaa.f & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h ^ aaaaaaaaaaaaaaaa.i << aaaaaaaaaaaaaaaa.k >> aaaaaaaaaaaaaaaa.l ** aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n + ): + return True +-( +- 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 ```py ... -"some_string" -b"\\xa3" +'some_string' +b'\\xa3' Name None True @@ -439,133 +713,95 @@ 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 -1 ~int and not v1 ^ 123 + v2 | True (~int) and (not ((v1 ^ (123 + v2)) | True)) -+(really ** -(confusing ** ~(operator**-precedence))) -flags & ~select.EPOLLIN and waiters.write_task is not None ++really ** -confusing ** ~operator ** -precedence +flags & ~ select.EPOLLIN and waiters.write_task is not None lambda arg: None lambda a=True: a lambda a, b, c=True: a -lambda a, b, c=True, *, d=(1 << v2), e="str": a -lambda a, b, c=True, *vararg, d=(v1 << 2), e="str", **kwargs: a + b +lambda a, b, c=True, *, d=(1 << v2), e='str': a +lambda a, b, c=True, *vararg, d=(v1 << 2), e='str', **kwargs: a + b manylambdas = lambda x=lambda y=lambda z=1: z: y(): x() -foo = lambda port_id, -ignore_missing,: { - "port1": port1_resource, - "port2": port2_resource, -}[port_id] +foo = (lambda port_id, ignore_missing: {"port1": port1_resource, "port2": port2_resource}[port_id]) 1 if True else 2 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) -) -{"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} -{"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 +{'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 () (1,) (1, 2) (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"))} -{((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)} +{(i ** 2) for i in (1, 2, 3)} +{(i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))} +{((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)} [i for i in (1, 2, 3)] -[(i**2) for i in (1, 2, 3)] -[(i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))] -[((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)] +[(i ** 2) for i in (1, 2, 3)] +[(i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))] +[((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)] {i: 0 for i in (1, 2, 3)} -{i: j for i, j in ((1, "a"), (2, "b"), (3, "c"))} +{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 -} +{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(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 +1 .real +1.0 .real ....__class__ 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] ] +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]]]], ] -xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = ( - classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) # type: ignore -) -xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = ( - classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) # type: ignore +xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore + sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__) ) -xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = ( - classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) +xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore + sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__) ) +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] @@ -593,53 +829,29 @@ 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"} +{'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 (Good, Bad, Ugly) (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"))) -(((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)) +((i ** 2) for i in (1, 2, 3)) +((i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))) +(((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)) (*starred,) -{ - "id": "1", - "type": "type", - "started_at": now(), - "ended_at": now() + timedelta(days=10), - "priority": 1, - "import_session_id": 1, - **kwargs, -} +{"id": "1","type": "type","started_at": now(),"ended_at": now() + timedelta(days=10),"priority": 1,"import_session_id": 1,**kwargs} a = (1,) -b = (1,) +b = 1, c = 1 d = (1,) + a + (2,) e = (1,).count(1) f = 1, *range(10) g = 1, *"ten" -what_is_up_with_those_new_coord_names = ( - (coord_names - + set(vars_to_create)) - + set(vars_to_remove) -) -what_is_up_with_those_new_coord_names = ( - (coord_names - | set(vars_to_create)) - - set(vars_to_remove) -) -result = session.query(models.Customer.id).filter( - models.Customer.account_id == account_id, - models.Customer.email == email_address, -).order_by(models.Customer.id.asc()).all() -result = session.query(models.Customer.id).filter( - models.Customer.account_id == account_id, - models.Customer.email == email_address, -).order_by( - models.Customer.id.asc(), -).all() +what_is_up_with_those_new_coord_names = (coord_names + set(vars_to_create)) + set(vars_to_remove) +what_is_up_with_those_new_coord_names = (coord_names | set(vars_to_create)) - set(vars_to_remove) +result = session.query(models.Customer.id).filter(models.Customer.account_id == account_id, models.Customer.email == email_address).order_by(models.Customer.id.asc()).all() +result = session.query(models.Customer.id).filter(models.Customer.account_id == account_id, models.Customer.email == email_address).order_by(models.Customer.id.asc(),).all() Ø = set() authors.łukasz.say_thanks() mapping = { @@ -649,146 +861,86 @@ mapping = { D: 0.1 * (10.0 / 12), } - def gen(): yield from outside_of_generator - a = yield - b = yield - c = yield - + a = (yield) + b = ((yield)) + c = (((yield))) async def f(): await some.complicated[0].call(with_args=(True or (1 is not 1))) - - -print(*[] or [1]) +print(* [] or [1]) print(**{1: 3} if False else {x: x for x in range(3)}) -print(*lambda x: x) -assert not Test, "Short message" -assert this is ComplexTest and not requirements.fit_in_a_single_line(force=False), ( - "Short message" -) -assert parens is TooMany -for (x,) in (1,), (2,), (3,): - ... -for y in (): - ... -for z in (i for i in (1, 2, 3)): - ... -for i in call(): - ... -for j in 1 + (2 + 3): - ... -while this and that: - ... -for ( - addr_family, - addr_type, - addr_proto, - addr_canonname, - addr_sockaddr, -) in socket.getaddrinfo("google.com", "http"): +print(* lambda x: x) +assert(not Test),("Short message") +assert this is ComplexTest and not requirements.fit_in_a_single_line(force=False), "Short message" +assert(((parens is TooMany))) +for x, in (1,), (2,), (3,): ... +for y in (): ... +for z in (i for i in (1, 2, 3)): ... +for i in (call()): ... +for j in (1 + (2 + 3)): ... +while(this and that): ... +for addr_family, addr_type, addr_proto, addr_canonname, addr_sockaddr in socket.getaddrinfo('google.com', 'http'): pass -a = ( - aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp - in qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz -) -a = ( - aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp - not in qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz -) -a = ( - aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp - is qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz -) -a = ( - aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp - is not qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz -) +a = aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp in qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz +a = aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp not in qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz +a = aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp is qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz +a = aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp is not qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz if ( - threading.current_thread() != threading.main_thread() - and threading.current_thread() != threading.main_thread() - or signal.getsignal(signal.SIGINT) != signal.default_int_handler + threading.current_thread() != threading.main_thread() and + threading.current_thread() != threading.main_thread() or + signal.getsignal(signal.SIGINT) != signal.default_int_handler ): return True if ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ): return True if ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ): return True if ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ): return True if ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ): return True if ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ): return True if ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa / aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - / aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ): return True if ( - ~aaaa.a - + aaaa.b - - aaaa.c * aaaa.d / aaaa.e - | aaaa.f & aaaa.g % aaaa.h ^ aaaa.i << aaaa.k >> aaaa.l**aaaa.m // aaaa.n + ~ aaaa.a + aaaa.b - aaaa.c * aaaa.d / aaaa.e | aaaa.f & aaaa.g % aaaa.h ^ aaaa.i << aaaa.k >> aaaa.l ** aaaa.m // aaaa.n ): return True if ( - ~aaaaaaaa.a - + aaaaaaaa.b - - aaaaaaaa.c @ aaaaaaaa.d / aaaaaaaa.e - | aaaaaaaa.f - & aaaaaaaa.g % aaaaaaaa.h - ^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l**aaaaaaaa.m // aaaaaaaa.n + ~ aaaaaaaa.a + aaaaaaaa.b - aaaaaaaa.c @ aaaaaaaa.d / aaaaaaaa.e | aaaaaaaa.f & aaaaaaaa.g % aaaaaaaa.h ^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l ** aaaaaaaa.m // aaaaaaaa.n ): return True if ( - ~aaaaaaaaaaaaaaaa.a - + aaaaaaaaaaaaaaaa.b - - aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e - | aaaaaaaaaaaaaaaa.f - & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h - ^ aaaaaaaaaaaaaaaa.i - << aaaaaaaaaaaaaaaa.k - >> aaaaaaaaaaaaaaaa.l**aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n + ~ aaaaaaaaaaaaaaaa.a + aaaaaaaaaaaaaaaa.b - aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e | aaaaaaaaaaaaaaaa.f & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h ^ aaaaaaaaaaaaaaaa.i << aaaaaaaaaaaaaaaa.k >> aaaaaaaaaaaaaaaa.l ** aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n ): return True -( - 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 ``` ## Black Output diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff2_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff2_py.snap deleted file mode 100644 index dff3ec9ad894d..0000000000000 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff2_py.snap +++ /dev/null @@ -1,205 +0,0 @@ ---- -source: crates/ruff_python_formatter/src/lib.rs -expression: snapshot -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff2.py ---- -## Input - -```py -import pytest - -TmSt = 1 -TmEx = 2 - -# fmt: off - -# Test data: -# Position, Volume, State, TmSt/TmEx/None, [call, [arg1...]] - -@pytest.mark.parametrize('test', [ - - # Test don't manage the volume - [ - ('stuff', 'in') - ], -]) -def test_fader(test): - pass - -def check_fader(test): - - pass - -def verify_fader(test): - # misaligned comment - pass - -def verify_fader(test): - """Hey, ho.""" - assert test.passed() - -def test_calculate_fades(): - calcs = [ - # one is zero/none - (0, 4, 0, 0, 10, 0, 0, 6, 10), - (None, 4, 0, 0, 10, 0, 0, 6, 10), - ] - -# fmt: on -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -3,38 +3,42 @@ - TmSt = 1 - TmEx = 2 - -+ - # fmt: off - - # Test data: - # Position, Volume, State, TmSt/TmEx/None, [call, [arg1...]] - --@pytest.mark.parametrize('test', [ -- -- # Test don't manage the volume -+@pytest.mark.parametrize( -+ "test", - [ -- ('stuff', 'in') -+ # Test don't manage the volume -+ [("stuff", "in")], - ], --]) -+) - def test_fader(test): - pass - -+ - def check_fader(test): -+ pass - -- pass - - def verify_fader(test): -- # misaligned comment -+ # misaligned comment - pass - -+ - def verify_fader(test): - """Hey, ho.""" - assert test.passed() - -+ - def test_calculate_fades(): - calcs = [ -- # one is zero/none -- (0, 4, 0, 0, 10, 0, 0, 6, 10), -- (None, 4, 0, 0, 10, 0, 0, 6, 10), -+ (# one is zero/none -+ 0, 4, 0, 0, 10, 0, 0, 6, 10), -+ (None, 4, 0, 0, 10, 0, 0, 6, 10), - ] - - # fmt: on -``` - -## Ruff Output - -```py -import pytest - -TmSt = 1 -TmEx = 2 - - -# fmt: off - -# Test data: -# Position, Volume, State, TmSt/TmEx/None, [call, [arg1...]] - -@pytest.mark.parametrize( - "test", - [ - # Test don't manage the volume - [("stuff", "in")], - ], -) -def test_fader(test): - pass - - -def check_fader(test): - pass - - -def verify_fader(test): - # misaligned comment - pass - - -def verify_fader(test): - """Hey, ho.""" - assert test.passed() - - -def test_calculate_fades(): - calcs = [ - (# one is zero/none - 0, 4, 0, 0, 10, 0, 0, 6, 10), - (None, 4, 0, 0, 10, 0, 0, 6, 10), - ] - -# fmt: on -``` - -## Black Output - -```py -import pytest - -TmSt = 1 -TmEx = 2 - -# fmt: off - -# Test data: -# Position, Volume, State, TmSt/TmEx/None, [call, [arg1...]] - -@pytest.mark.parametrize('test', [ - - # Test don't manage the volume - [ - ('stuff', 'in') - ], -]) -def test_fader(test): - pass - -def check_fader(test): - - pass - -def verify_fader(test): - # misaligned comment - pass - -def verify_fader(test): - """Hey, ho.""" - assert test.passed() - -def test_calculate_fades(): - calcs = [ - # one is zero/none - (0, 4, 0, 0, 10, 0, 0, 6, 10), - (None, 4, 0, 0, 10, 0, 0, 6, 10), - ] - -# fmt: on -``` - - diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff3_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff3_py.snap index 44a115b964146..772a2828adf67 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff3_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff3_py.snap @@ -30,29 +30,14 @@ x = [ ```diff --- Black +++ Ruff -@@ -1,14 +1,18 @@ - # fmt: off - x = [ -- 1, 2, -- 3, 4, -+ 1, -+ 2, -+ 3, -+ 4, - ] - # fmt: on - - # fmt: off - x = [ -- 1, 2, -- 3, 4, -+ 1, -+ 2, -+ 3, -+ 4, +@@ -12,4 +12,6 @@ ] # fmt: on +-x = [1, 2, 3, 4] ++x = [ ++ 1, 2, 3, 4 ++] ``` ## Ruff Output @@ -60,23 +45,21 @@ x = [ ```py # fmt: off x = [ - 1, - 2, - 3, - 4, + 1, 2, + 3, 4, ] # fmt: on # fmt: off x = [ - 1, - 2, - 3, - 4, + 1, 2, + 3, 4, ] # fmt: on -x = [1, 2, 3, 4] +x = [ + 1, 2, 3, 4 +] ``` ## Black Output diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff4_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff4_py.snap index 68ffcf8c4e5be..cda65520cfeb1 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff4_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff4_py.snap @@ -26,52 +26,48 @@ def f(): pass ```diff --- Black +++ Ruff -@@ -1,8 +1,12 @@ - # fmt: off --@test([ -- 1, 2, -- 3, 4, --]) -+@test( -+ [ -+ 1, -+ 2, -+ 3, -+ 4, -+ ] -+) +@@ -4,17 +4,10 @@ + 3, 4, + ]) # fmt: on - def f(): - pass +-def f(): +- pass +- ++def f(): pass + +-@test( +- [ +- 1, +- 2, +- 3, +- 4, +- ] +-) +-def f(): +- pass ++@test([ ++ 1, 2, ++ 3, 4, ++]) ++def f(): pass ``` ## Ruff Output ```py # fmt: off -@test( - [ - 1, - 2, - 3, - 4, - ] -) +@test([ + 1, 2, + 3, 4, +]) # fmt: on -def f(): - pass - +def f(): pass -@test( - [ - 1, - 2, - 3, - 4, - ] -) -def f(): - pass +@test([ + 1, 2, + 3, 4, +]) +def f(): pass ``` ## Black Output diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff5_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff5_py.snap index 00bdc220b3c42..08c50822634a6 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff5_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff5_py.snap @@ -97,87 +97,45 @@ elif unformatted: ```diff --- Black +++ Ruff -@@ -3,10 +3,8 @@ - entry_points={ - # fmt: off - "console_scripts": [ -- "foo-bar" -- "=foo.bar.:main", -- # fmt: on -- ] # Includes an formatted indentation. -+ "foo-bar" "=foo.bar.:main", -+ ], # Includes an formatted indentation. - }, - ) - -@@ -27,9 +25,8 @@ - # Regression test for https://github.com/psf/black/issues/3026. - def test_func(): - # yapf: disable -- if unformatted( args ): -+ if unformatted(args): - return True -- # yapf: enable - elif b: - return True - -@@ -39,10 +36,10 @@ - # Regression test for https://github.com/psf/black/issues/2567. - if True: - # fmt: off -- for _ in range( 1 ): -- # fmt: on -- print ( "This won't be formatted" ) -- print ( "This won't be formatted either" ) -+ for _ in range(1): -+ # fmt: on -+ print("This won't be formatted") -+ print("This won't be formatted either") +@@ -44,7 +44,7 @@ + print ( "This won't be formatted" ) + print ( "This won't be formatted either" ) else: - print("This will be formatted") +- print("This will be formatted") ++ print ( "This will be formatted" ) -@@ -52,14 +49,11 @@ - async def call(param): - if param: - # fmt: off -- if param[0:4] in ( -- "ABCD", "EFGH" -- ) : -+ if param[0:4] in ("ABCD", "EFGH"): - # fmt: on -- print ( "This won't be formatted" ) -- -+ print("This won't be formatted") + + # Regression test for https://github.com/psf/black/issues/3184. +@@ -61,7 +61,7 @@ elif param[0:4] in ("ZZZZ",): -- print ( "This won't be formatted either" ) -+ print("This won't be formatted either") + print ( "This won't be formatted either" ) - print("This will be formatted") +- print("This will be formatted") ++ print ( "This will be formatted" ) -@@ -68,20 +62,19 @@ - class Named(t.Protocol): - # fmt: off - @property -- def this_wont_be_formatted ( self ) -> str: ... -+ def this_wont_be_formatted(self) -> str: -+ ... + # Regression test for https://github.com/psf/black/issues/2985. +@@ -70,11 +70,8 @@ + @property + def this_wont_be_formatted ( self ) -> str: ... +- class Factory(t.Protocol): - def this_will_be_formatted(self, **kwargs) -> Named: - ... +- def this_will_be_formatted(self, **kwargs) -> Named: +- ... - ++ def this_will_be_formatted ( self, **kwargs ) -> Named: ... # fmt: on - # Regression test for https://github.com/psf/black/issues/3436. - if x: +@@ -83,5 +80,5 @@ return x --# fmt: off --elif unformatted: -+elif unformatted: - # fmt: on - will_be_formatted() + # fmt: off + elif unformatted: +- # fmt: on +- will_be_formatted() ++# fmt: on ++ will_be_formatted () ``` ## Ruff Output @@ -188,8 +146,10 @@ setup( entry_points={ # fmt: off "console_scripts": [ - "foo-bar" "=foo.bar.:main", - ], # Includes an formatted indentation. + "foo-bar" + "=foo.bar.:main", + # fmt: on + ] # Includes an formatted indentation. }, ) @@ -210,8 +170,9 @@ run( # Regression test for https://github.com/psf/black/issues/3026. def test_func(): # yapf: disable - if unformatted(args): + if unformatted( args ): return True + # yapf: enable elif b: return True @@ -221,12 +182,12 @@ def test_func(): # Regression test for https://github.com/psf/black/issues/2567. if True: # fmt: off - for _ in range(1): - # fmt: on - print("This won't be formatted") - print("This won't be formatted either") + for _ in range( 1 ): + # fmt: on + print ( "This won't be formatted" ) + print ( "This won't be formatted either" ) else: - print("This will be formatted") + print ( "This will be formatted" ) # Regression test for https://github.com/psf/black/issues/3184. @@ -234,35 +195,36 @@ class A: async def call(param): if param: # fmt: off - if param[0:4] in ("ABCD", "EFGH"): + if param[0:4] in ( + "ABCD", "EFGH" + ) : # fmt: on - print("This won't be formatted") + print ( "This won't be formatted" ) + elif param[0:4] in ("ZZZZ",): - print("This won't be formatted either") + print ( "This won't be formatted either" ) - print("This will be formatted") + print ( "This will be formatted" ) # Regression test for https://github.com/psf/black/issues/2985. class Named(t.Protocol): # fmt: off @property - def this_wont_be_formatted(self) -> str: - ... - + def this_wont_be_formatted ( self ) -> str: ... class Factory(t.Protocol): - def this_will_be_formatted(self, **kwargs) -> Named: - ... + def this_will_be_formatted ( self, **kwargs ) -> Named: ... # fmt: on # Regression test for https://github.com/psf/black/issues/3436. if x: return x -elif unformatted: - # fmt: on - will_be_formatted() +# fmt: off +elif unformatted: +# fmt: on + will_be_formatted () ``` ## Black Output 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 ac5e1e5c4d034..2bace21cc8599 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 @@ -199,213 +199,199 @@ d={'a':1, ```diff --- Black +++ Ruff -@@ -5,39 +5,53 @@ - from third_party import X, Y, Z +@@ -4,18 +4,17 @@ - from library import some_connection, some_decorator -+# fmt: off -+from third_party import X, Y, Z + from third_party import X, Y, Z --# fmt: off --from third_party import (X, -- Y, Z) +-from library import some_connection, some_decorator +- ++from library import some_connection, \ ++ some_decorator + # fmt: off + from third_party import (X, + Y, Z) # fmt: on -f"trigger 3.6 mode" +f'trigger 3.6 mode' -+ -+ # Comment 1 # Comment 2 -- +- # fmt: off def func_no_args(): -- a; b; c -- if True: raise RuntimeError -- if False: ... -- for i in range(10): -- print(i) -- continue -- exec('new-style exec', {}, {}) -- return None -+ a -+ b -+ c -+ if True: -+ raise RuntimeError -+ if False: -+ ... -+ for i in range(10): -+ print(i) -+ continue -+ exec("new-style exec", {}, {}) -+ return None -+ -+ - async def coroutine(arg, exec=False): -- 'Single-line docstring. Multiline is harder to reformat.' -- async with some_connection() as conn: -- await conn.do_what_i_mean('SELECT bobby, tables FROM xkcd', timeout=2) -- await asyncio.sleep(1) -+ "Single-line docstring. Multiline is harder to reformat." -+ async with some_connection() as conn: -+ await conn.do_what_i_mean("SELECT bobby, tables FROM xkcd", timeout=2) -+ await asyncio.sleep(1) -+ -+ - @asyncio.coroutine --@some_decorator( --with_args=True, --many_args=[1,2,3] --) --def function_signature_stress_test(number:int,no_annotation=None,text:str='default',* ,debug:bool=False,**kwargs) -> str: -- return text[number:-1] -+@some_decorator(with_args=True, many_args=[1, 2, 3]) -+def function_signature_stress_test( -+ number: int, -+ no_annotation=None, -+ text: str = "default", -+ *, -+ debug: bool = False, -+ **kwargs, -+) -> str: -+ return text[number:-1] -+ -+ + a; b; c +@@ -39,34 +38,16 @@ + def function_signature_stress_test(number:int,no_annotation=None,text:str='default',* ,debug:bool=False,**kwargs) -> str: + return text[number:-1] # fmt: on - def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r""): - offset = attr.ib(default=attr.Factory(lambda: _r.uniform(1, 2))) -@@ -64,55 +78,55 @@ - +-def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r""): +- offset = attr.ib(default=attr.Factory(lambda: _r.uniform(1, 2))) +- assert task._cancel_stack[: len(old_stack)] == old_stack +- +- +-def spaces_types( +- a: int = 1, +- b: tuple = (), +- c: list = [], +- d: dict = {}, +- e: bool = True, +- f: int = -1, +- g: int = 1 if False else 2, +- h: str = "", +- i: str = r"", +-): +- ... +- +- +-def spaces2(result=_core.Value(None)): +- ... +- +- ++def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r''): ++ offset = attr.ib(default=attr.Factory( lambda: _r.uniform(1, 2))) ++ assert task._cancel_stack[:len(old_stack)] == old_stack ++def spaces_types(a: int = 1, b: tuple = (), c: list = [], d: dict = {}, e: bool = True, f: int = -1, g: int = 1 if False else 2, h: str = "", i: str = r''): ... ++def spaces2(result= _core.Value(None)): ++ ... something = { # fmt: off -- key: 'value', -+ key: "value", + key: 'value', } - +- def subscriptlist(): atom[ - # fmt: off -- 'some big and', -- 'complex subscript', -+ "some big and", -+ "complex subscript", +@@ -74,17 +55,14 @@ + 'some big and', + 'complex subscript', # fmt: on - goes + here, -+ goes -+ + here, - andhere, - ] - +- andhere, +- ] +- ++ goes + here, andhere, ++ ] def import_as_names(): # fmt: off -- from hello import a, b -- 'unformatted' -+ from hello import a, b -+ -+ "unformatted" + from hello import a, b + 'unformatted' # fmt: on - +- def testlist_star_expr(): # fmt: off -- a , b = *hello -- 'unformatted' -+ a, b = *hello -+ "unformatted" +@@ -92,18 +70,16 @@ + 'unformatted' # fmt: on - +- def yield_expr(): # fmt: off yield hello -- 'unformatted' -+ "unformatted" + 'unformatted' # fmt: on - "formatted" +- "formatted" ++ 'formatted' # fmt: off -- ( yield hello ) -- 'unformatted' -+ (yield hello) -+ "unformatted" + ( yield hello ) + 'unformatted' # fmt: on - +- def example(session): # fmt: off -- result = session\ -- .query(models.Customer.id)\ -- .filter(models.Customer.account_id == account_id, -- models.Customer.email == email_address)\ -- .order_by(models.Customer.id.asc())\ -- .all() -+ result = session.query(models.Customer.id).filter( -+ models.Customer.account_id == account_id, -+ models.Customer.email == email_address, -+ ).order_by(models.Customer.id.asc()).all() +@@ -114,8 +90,6 @@ + .order_by(models.Customer.id.asc())\ + .all() # fmt: on +- +- + def off_and_on_without_data(): + """All comments here are technically on the same prefix. - -@@ -123,7 +137,7 @@ +@@ -123,12 +97,12 @@ """ # fmt: off - # hey, that won't work -+ #hey, that won't work ++ #hey, that won't work ++ ++ # fmt: on pass -@@ -133,10 +147,10 @@ +- +- + def on_and_off_broken(): """Another known limitation.""" # fmt: on - # fmt: off -- this=should.not_be.formatted() -- and_=indeed . it is not formatted -- because . the . handling . inside . generate_ignored_nodes() -- now . considers . multiple . fmt . directives . within . one . prefix -+ this = should.not_be.formatted() -+ and_ = indeed.it is not formatted -+ because.the.handling.inside.generate_ignored_nodes() -+ now.considers.multiple.fmt.directives.within.one.prefix +@@ -139,19 +113,12 @@ + now . considers . multiple . fmt . directives . within . one . prefix # fmt: on # fmt: off - # ...but comments still get reformatted even though they should not be -@@ -154,9 +168,7 @@ - ) +- # ...but comments still get reformatted even though they should not be ++ # ...but comments still get reformatted even though they should not be + # fmt: on +- +- + def long_lines(): + if True: + typedargslist.extend( +- gen_annotated_params( +- ast_args.kwonlyargs, +- ast_args.kw_defaults, +- parameters, +- implicit_default=True, +- ) ++ gen_annotated_params(ast_args.kwonlyargs, ast_args.kw_defaults, parameters, implicit_default=True) ) # fmt: off -- a = ( -- unnecessary_bracket() -- ) -+ a = unnecessary_bracket() - # fmt: on - _type_comment_re = re.compile( - r""" -@@ -179,7 +191,8 @@ - $ - """, - # fmt: off -- re.MULTILINE|re.VERBOSE -+ re.MULTILINE -+ | re.VERBOSE, + a = ( +@@ -182,24 +149,19 @@ + re.MULTILINE|re.VERBOSE # fmt: on ) - -@@ -217,8 +230,7 @@ - xxxxxxxxxx_xxxxxxxxxxx_xxxxxxx_xxxxxxxxx=5, +- +- + def single_literal_yapf_disable(): + """Black does not support this.""" +- BAZ = {(1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)} # yapf: disable +- +- ++ BAZ = { ++ (1, 2, 3, 4), ++ (5, 6, 7, 8), ++ (9, 10, 11, 12) ++ } # yapf: disable + cfg.rule( +- "Default", +- "address", ++ "Default", "address", + xxxx_xxxx=["xxx-xxxxxx-xxxxxxxxxx"], +- xxxxxx="xx_xxxxx", +- xxxxxxx="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", +- xxxxxxxxx_xxxx=True, +- xxxxxxxx_xxxxxxxxxx=False, +- xxxxxx_xxxxxx=2, +- xxxxxx_xxxxx_xxxxxxxx=70, +- xxxxxx_xxxxxx_xxxxx=True, ++ xxxxxx="xx_xxxxx", xxxxxxx="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", ++ xxxxxxxxx_xxxx=True, xxxxxxxx_xxxxxxxxxx=False, ++ xxxxxx_xxxxxx=2, xxxxxx_xxxxx_xxxxxxxx=70, xxxxxx_xxxxxx_xxxxx=True, + # fmt: off + xxxxxxx_xxxxxxxxxxxx={ + "xxxxxxxx": { +@@ -214,7 +176,7 @@ + }, + }, + # fmt: on +- xxxxxxxxxx_xxxxxxxxxxx_xxxxxxx_xxxxxxxxx=5, ++ xxxxxxxxxx_xxxxxxxxxxx_xxxxxxx_xxxxxxxxx=5 ) # fmt: off --yield 'hello' -+yield "hello" - # No formatting to the end of the file --l=[1,2,3] --d={'a':1, -- 'b':2} -+l = [1, 2, 3] -+d = {"a": 1, "b": 2} + yield 'hello' ``` ## Ruff Output @@ -417,132 +403,92 @@ import sys from third_party import X, Y, Z -from library import some_connection, some_decorator +from library import some_connection, \ + some_decorator # fmt: off -from third_party import X, Y, Z - +from third_party import (X, + Y, Z) # fmt: on f'trigger 3.6 mode' - - # Comment 1 # Comment 2 # fmt: off def func_no_args(): - a - b - c - if True: - raise RuntimeError - if False: - ... - for i in range(10): - print(i) - continue - exec("new-style exec", {}, {}) - return None - - + a; b; c + if True: raise RuntimeError + if False: ... + for i in range(10): + print(i) + continue + exec('new-style exec', {}, {}) + return None async def coroutine(arg, exec=False): - "Single-line docstring. Multiline is harder to reformat." - async with some_connection() as conn: - await conn.do_what_i_mean("SELECT bobby, tables FROM xkcd", timeout=2) - await asyncio.sleep(1) - - + 'Single-line docstring. Multiline is harder to reformat.' + async with some_connection() as conn: + await conn.do_what_i_mean('SELECT bobby, tables FROM xkcd', timeout=2) + await asyncio.sleep(1) @asyncio.coroutine -@some_decorator(with_args=True, many_args=[1, 2, 3]) -def function_signature_stress_test( - number: int, - no_annotation=None, - text: str = "default", - *, - debug: bool = False, - **kwargs, -) -> str: - return text[number:-1] - - +@some_decorator( +with_args=True, +many_args=[1,2,3] +) +def function_signature_stress_test(number:int,no_annotation=None,text:str='default',* ,debug:bool=False,**kwargs) -> str: + return text[number:-1] # fmt: on -def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r""): - offset = attr.ib(default=attr.Factory(lambda: _r.uniform(1, 2))) - assert task._cancel_stack[: len(old_stack)] == old_stack - - -def spaces_types( - a: int = 1, - b: tuple = (), - c: list = [], - d: dict = {}, - e: bool = True, - f: int = -1, - g: int = 1 if False else 2, - h: str = "", - i: str = r"", -): - ... - - -def spaces2(result=_core.Value(None)): - ... - - +def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r''): + offset = attr.ib(default=attr.Factory( lambda: _r.uniform(1, 2))) + assert task._cancel_stack[:len(old_stack)] == old_stack +def spaces_types(a: int = 1, b: tuple = (), c: list = [], d: dict = {}, e: bool = True, f: int = -1, g: int = 1 if False else 2, h: str = "", i: str = r''): ... +def spaces2(result= _core.Value(None)): + ... something = { # fmt: off - key: "value", + key: 'value', } - def subscriptlist(): atom[ # fmt: off - "some big and", - "complex subscript", + 'some big and', + 'complex subscript', # fmt: on - goes - + here, - andhere, - ] - + goes + here, andhere, + ] def import_as_names(): # fmt: off - from hello import a, b - - "unformatted" + from hello import a, b + 'unformatted' # fmt: on - def testlist_star_expr(): # fmt: off - a, b = *hello - "unformatted" + a , b = *hello + 'unformatted' # fmt: on - def yield_expr(): # fmt: off yield hello - "unformatted" + 'unformatted' # fmt: on - "formatted" + 'formatted' # fmt: off - (yield hello) - "unformatted" + ( yield hello ) + 'unformatted' # fmt: on - def example(session): # fmt: off - result = session.query(models.Customer.id).filter( - models.Customer.account_id == account_id, - models.Customer.email == email_address, - ).order_by(models.Customer.id.asc()).all() + result = session\ + .query(models.Customer.id)\ + .filter(models.Customer.account_id == account_id, + models.Customer.email == email_address)\ + .order_by(models.Customer.id.asc())\ + .all() # fmt: on - - def off_and_on_without_data(): """All comments here are technically on the same prefix. @@ -550,38 +496,33 @@ def off_and_on_without_data(): """ # fmt: off - #hey, that won't work - # fmt: on - pass + #hey, that won't work + # fmt: on + pass def on_and_off_broken(): """Another known limitation.""" # fmt: on # fmt: off - this = should.not_be.formatted() - and_ = indeed.it is not formatted - because.the.handling.inside.generate_ignored_nodes() - now.considers.multiple.fmt.directives.within.one.prefix + this=should.not_be.formatted() + and_=indeed . it is not formatted + because . the . handling . inside . generate_ignored_nodes() + now . considers . multiple . fmt . directives . within . one . prefix # fmt: on # fmt: off - # ...but comments still get reformatted even though they should not be + # ...but comments still get reformatted even though they should not be # fmt: on - - def long_lines(): if True: typedargslist.extend( - gen_annotated_params( - ast_args.kwonlyargs, - ast_args.kw_defaults, - parameters, - implicit_default=True, - ) + gen_annotated_params(ast_args.kwonlyargs, ast_args.kw_defaults, parameters, implicit_default=True) ) # fmt: off - a = unnecessary_bracket() + a = ( + unnecessary_bracket() + ) # fmt: on _type_comment_re = re.compile( r""" @@ -604,28 +545,22 @@ def long_lines(): $ """, # fmt: off - re.MULTILINE - | re.VERBOSE, + re.MULTILINE|re.VERBOSE # fmt: on ) - - def single_literal_yapf_disable(): """Black does not support this.""" - BAZ = {(1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)} # yapf: disable - - + BAZ = { + (1, 2, 3, 4), + (5, 6, 7, 8), + (9, 10, 11, 12) + } # yapf: disable cfg.rule( - "Default", - "address", + "Default", "address", xxxx_xxxx=["xxx-xxxxxx-xxxxxxxxxx"], - xxxxxx="xx_xxxxx", - xxxxxxx="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", - xxxxxxxxx_xxxx=True, - xxxxxxxx_xxxxxxxxxx=False, - xxxxxx_xxxxxx=2, - xxxxxx_xxxxx_xxxxxxxx=70, - xxxxxx_xxxxxx_xxxxx=True, + xxxxxx="xx_xxxxx", xxxxxxx="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + xxxxxxxxx_xxxx=True, xxxxxxxx_xxxxxxxxxx=False, + xxxxxx_xxxxxx=2, xxxxxx_xxxxx_xxxxxxxx=70, xxxxxx_xxxxxx_xxxxx=True, # fmt: off xxxxxxx_xxxxxxxxxxxx={ "xxxxxxxx": { @@ -640,13 +575,14 @@ cfg.rule( }, }, # fmt: on - xxxxxxxxxx_xxxxxxxxxxx_xxxxxxx_xxxxxxxxx=5, + xxxxxxxxxx_xxxxxxxxxxx_xxxxxxx_xxxxxxxxx=5 ) # fmt: off -yield "hello" +yield 'hello' # No formatting to the end of the file -l = [1, 2, 3] -d = {"a": 1, "b": 2} +l=[1,2,3] +d={'a':1, + 'b':2} ``` ## Black Output diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip2_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip2_py.snap index 2bd8d5bf4fd67..99d137293e94d 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip2_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip2_py.snap @@ -16,39 +16,28 @@ l3 = ["I have", "trailing comma", "so I should be braked",] ```diff --- Black +++ Ruff -@@ -3,7 +3,11 @@ - "into multiple lines", - "because it is way too long", - ] --l2 = ["But this list shouldn't", "even though it also has", "way too many characters in it"] # fmt: skip -+l2 = [ -+ "But this list shouldn't", -+ "even though it also has", -+ "way too many characters in it", -+] # fmt: skip - l3 = [ - "I have", - "trailing comma", +@@ -1,11 +1,3 @@ +-l1 = [ +- "This list should be broken up", +- "into multiple lines", +- "because it is way too long", +-] ++l1 = ["This list should be broken up", "into multiple lines", "because it is way too long"] + l2 = ["But this list shouldn't", "even though it also has", "way too many characters in it"] # fmt: skip +-l3 = [ +- "I have", +- "trailing comma", +- "so I should be braked", +-] ++l3 = ["I have", "trailing comma", "so I should be braked",] ``` ## Ruff Output ```py -l1 = [ - "This list should be broken up", - "into multiple lines", - "because it is way too long", -] -l2 = [ - "But this list shouldn't", - "even though it also has", - "way too many characters in it", -] # fmt: skip -l3 = [ - "I have", - "trailing comma", - "so I should be braked", -] +l1 = ["This list should be broken up", "into multiple lines", "because it is way too long"] +l2 = ["But this list shouldn't", "even though it also has", "way too many characters in it"] # fmt: skip +l3 = ["I have", "trailing comma", "so I should be braked",] ``` ## Black Output diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip3_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip3_py.snap index 2e54d4b721446..6dd878bc1e3d0 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip3_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip3_py.snap @@ -20,31 +20,31 @@ f = ["This is a very long line that should be formatted into a clearer line ", " ```diff --- Black +++ Ruff -@@ -1,7 +1,7 @@ - a = 3 +@@ -1,10 +1,7 @@ +-a = 3 ++a = 3 # fmt: off --b, c = 1, 2 --d = 6 # fmt: skip -+b, c = 1, 2 -+d = 6 # fmt: skip + b, c = 1, 2 + d = 6 # fmt: skip e = 5 # fmt: on - f = [ +-f = [ +- "This is a very long line that should be formatted into a clearer line ", +- "by rearranging.", +-] ++f = ["This is a very long line that should be formatted into a clearer line ", "by rearranging."] ``` ## Ruff Output ```py -a = 3 +a = 3 # fmt: off -b, c = 1, 2 -d = 6 # fmt: skip +b, c = 1, 2 +d = 6 # fmt: skip e = 5 # fmt: on -f = [ - "This is a very long line that should be formatted into a clearer line ", - "by rearranging.", -] +f = ["This is a very long line that should be formatted into a clearer line ", "by rearranging."] ``` ## Black Output diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip4_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip4_py.snap new file mode 100644 index 0000000000000..745448d84db32 --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip4_py.snap @@ -0,0 +1,51 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip4.py +--- +## Input + +```py +a = 2 +# fmt: skip +l = [1, 2, 3,] +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,7 +1,3 @@ +-a = 2 ++a = 2 + # fmt: skip +-l = [ +- 1, +- 2, +- 3, +-] ++l = [1, 2, 3,] +``` + +## Ruff Output + +```py +a = 2 +# fmt: skip +l = [1, 2, 3,] +``` + +## Black Output + +```py +a = 2 +# fmt: skip +l = [ + 1, + 2, + 3, +] +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip5_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip5_py.snap index 96710762110d5..99b717aa89de4 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip5_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip5_py.snap @@ -22,24 +22,26 @@ else: ```diff --- Black +++ Ruff -@@ -1,9 +1,5 @@ - a, b, c = 3, 4, 5 --if ( +@@ -1,6 +1,6 @@ +-a, b, c = 3, 4, 5 ++a, b, c = 3, 4, 5 + if ( - a == 3 -- and b != 9 # fmt: skip -- and c is not None --): -+if a == 3 and b != 9 and c is not None: # fmt: skip - print("I'm good!") - else: - print("I'm bad") ++ a == 3 + and b != 9 # fmt: skip + and c is not None + ): ``` ## Ruff Output ```py -a, b, c = 3, 4, 5 -if a == 3 and b != 9 and c is not None: # fmt: skip +a, b, c = 3, 4, 5 +if ( + a == 3 + and b != 9 # fmt: skip + and c is not None +): print("I'm good!") else: print("I'm bad") diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip7_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip7_py.snap index caacad9a0e874..e9d2293265937 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip7_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip7_py.snap @@ -18,11 +18,12 @@ d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasu --- Black +++ Ruff @@ -1,4 +1,4 @@ - a = "this is some code" +-a = "this is some code" -b = 5 # fmt:skip -c = 9 # fmt: skip -d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" # fmt:skip -+b = 5 #fmt:skip ++a = "this is some code" ++b = 5 #fmt:skip +c = 9 #fmt: skip +d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" #fmt:skip ``` @@ -30,8 +31,8 @@ d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasu ## Ruff Output ```py -a = "this is some code" -b = 5 #fmt:skip +a = "this is some code" +b = 5 #fmt:skip c = 9 #fmt: skip d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" #fmt:skip ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip8_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip8_py.snap deleted file mode 100644 index 98e84a118fbeb..0000000000000 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip8_py.snap +++ /dev/null @@ -1,293 +0,0 @@ ---- -source: crates/ruff_python_formatter/src/lib.rs -expression: snapshot -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip8.py ---- -## Input - -```py -# Make sure a leading comment is not removed. -def some_func( unformatted, args ): # fmt: skip - print("I am some_func") - return 0 - # Make sure this comment is not removed. - - -# Make sure a leading comment is not removed. -async def some_async_func( unformatted, args): # fmt: skip - print("I am some_async_func") - await asyncio.sleep(1) - - -# Make sure a leading comment is not removed. -class SomeClass( Unformatted, SuperClasses ): # fmt: skip - def some_method( self, unformatted, args ): # fmt: skip - print("I am some_method") - return 0 - - async def some_async_method( self, unformatted, args ): # fmt: skip - print("I am some_async_method") - await asyncio.sleep(1) - - -# Make sure a leading comment is not removed. -if unformatted_call( args ): # fmt: skip - print("First branch") - # Make sure this is not removed. -elif another_unformatted_call( args ): # fmt: skip - print("Second branch") -else : # fmt: skip - print("Last branch") - - -while some_condition( unformatted, args ): # fmt: skip - print("Do something") - - -for i in some_iter( unformatted, args ): # fmt: skip - print("Do something") - - -async def test_async_for(): - async for i in some_async_iter( unformatted, args ): # fmt: skip - print("Do something") - - -try : # fmt: skip - some_call() -except UnformattedError as ex: # fmt: skip - handle_exception() -finally : # fmt: skip - finally_call() - - -with give_me_context( unformatted, args ): # fmt: skip - print("Do something") - - -async def test_async_with(): - async with give_me_async_context( unformatted, args ): # fmt: skip - print("Do something") -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -1,62 +1,62 @@ - # Make sure a leading comment is not removed. --def some_func( unformatted, args ): # fmt: skip -+def some_func(unformatted, args): # fmt: skip - print("I am some_func") - return 0 - # Make sure this comment is not removed. - - - # Make sure a leading comment is not removed. --async def some_async_func( unformatted, args): # fmt: skip -+async def some_async_func(unformatted, args): # fmt: skip - print("I am some_async_func") - await asyncio.sleep(1) - - - # Make sure a leading comment is not removed. --class SomeClass( Unformatted, SuperClasses ): # fmt: skip -- def some_method( self, unformatted, args ): # fmt: skip -+class SomeClass(Unformatted, SuperClasses): # fmt: skip -+ def some_method(self, unformatted, args): # fmt: skip - print("I am some_method") - return 0 - -- async def some_async_method( self, unformatted, args ): # fmt: skip -+ async def some_async_method(self, unformatted, args): # fmt: skip - print("I am some_async_method") - await asyncio.sleep(1) - - - # Make sure a leading comment is not removed. --if unformatted_call( args ): # fmt: skip -+if unformatted_call(args): # fmt: skip - print("First branch") - # Make sure this is not removed. --elif another_unformatted_call( args ): # fmt: skip -+elif another_unformatted_call(args): # fmt: skip - print("Second branch") --else : # fmt: skip -+else: # fmt: skip - print("Last branch") - - --while some_condition( unformatted, args ): # fmt: skip -+while some_condition(unformatted, args): # fmt: skip - print("Do something") - - --for i in some_iter( unformatted, args ): # fmt: skip -+for i in some_iter(unformatted, args): # fmt: skip - print("Do something") - - - async def test_async_for(): -- async for i in some_async_iter( unformatted, args ): # fmt: skip -+ async for i in some_async_iter(unformatted, args): # fmt: skip - print("Do something") - - --try : # fmt: skip -+try: # fmt: skip - some_call() --except UnformattedError as ex: # fmt: skip -+except UnformattedError as ex: - handle_exception() --finally : # fmt: skip -+finally: # fmt: skip - finally_call() - - --with give_me_context( unformatted, args ): # fmt: skip -+with give_me_context(unformatted, args): # fmt: skip - print("Do something") - - - async def test_async_with(): -- async with give_me_async_context( unformatted, args ): # fmt: skip -+ async with give_me_async_context(unformatted, args): # fmt: skip - print("Do something") -``` - -## Ruff Output - -```py -# Make sure a leading comment is not removed. -def some_func(unformatted, args): # fmt: skip - print("I am some_func") - return 0 - # Make sure this comment is not removed. - - -# Make sure a leading comment is not removed. -async def some_async_func(unformatted, args): # fmt: skip - print("I am some_async_func") - await asyncio.sleep(1) - - -# Make sure a leading comment is not removed. -class SomeClass(Unformatted, SuperClasses): # fmt: skip - def some_method(self, unformatted, args): # fmt: skip - print("I am some_method") - return 0 - - async def some_async_method(self, unformatted, args): # fmt: skip - print("I am some_async_method") - await asyncio.sleep(1) - - -# Make sure a leading comment is not removed. -if unformatted_call(args): # fmt: skip - print("First branch") - # Make sure this is not removed. -elif another_unformatted_call(args): # fmt: skip - print("Second branch") -else: # fmt: skip - print("Last branch") - - -while some_condition(unformatted, args): # fmt: skip - print("Do something") - - -for i in some_iter(unformatted, args): # fmt: skip - print("Do something") - - -async def test_async_for(): - async for i in some_async_iter(unformatted, args): # fmt: skip - print("Do something") - - -try: # fmt: skip - some_call() -except UnformattedError as ex: - handle_exception() -finally: # fmt: skip - finally_call() - - -with give_me_context(unformatted, args): # fmt: skip - print("Do something") - - -async def test_async_with(): - async with give_me_async_context(unformatted, args): # fmt: skip - print("Do something") -``` - -## Black Output - -```py -# Make sure a leading comment is not removed. -def some_func( unformatted, args ): # fmt: skip - print("I am some_func") - return 0 - # Make sure this comment is not removed. - - -# Make sure a leading comment is not removed. -async def some_async_func( unformatted, args): # fmt: skip - print("I am some_async_func") - await asyncio.sleep(1) - - -# Make sure a leading comment is not removed. -class SomeClass( Unformatted, SuperClasses ): # fmt: skip - def some_method( self, unformatted, args ): # fmt: skip - print("I am some_method") - return 0 - - async def some_async_method( self, unformatted, args ): # fmt: skip - print("I am some_async_method") - await asyncio.sleep(1) - - -# Make sure a leading comment is not removed. -if unformatted_call( args ): # fmt: skip - print("First branch") - # Make sure this is not removed. -elif another_unformatted_call( args ): # fmt: skip - print("Second branch") -else : # fmt: skip - print("Last branch") - - -while some_condition( unformatted, args ): # fmt: skip - print("Do something") - - -for i in some_iter( unformatted, args ): # fmt: skip - print("Do something") - - -async def test_async_for(): - async for i in some_async_iter( unformatted, args ): # fmt: skip - print("Do something") - - -try : # fmt: skip - some_call() -except UnformattedError as ex: # fmt: skip - handle_exception() -finally : # fmt: skip - finally_call() - - -with give_me_context( unformatted, args ): # fmt: skip - print("Do something") - - -async def test_async_with(): - async with give_me_async_context( unformatted, args ): # fmt: skip - print("Do something") -``` - - diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function2_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function2_py.snap index 19a5d4092f4fe..dfd644f3c7417 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function2_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function2_py.snap @@ -66,30 +66,83 @@ with hmm_but_this_should_get_two_preceding_newlines(): ```diff --- Black +++ Ruff -@@ -5,7 +5,8 @@ +@@ -1,11 +1,11 @@ + def f( +- a, +- **kwargs, ++ a, ++ **kwargs, + ) -> A: with cache_dir(): if something: - result = CliRunner().invoke( +- result = CliRunner().invoke( - black.main, [str(src1), str(src2), "--diff", "--check"] -+ black.main, -+ [str(src1), str(src2), "--diff", "--check"], ++ result = ( ++ CliRunner().invoke(black.main, [str(src1), str(src2), "--diff", "--check"]) ) limited.append(-limited.pop()) # negate top return A( +@@ -13,34 +13,24 @@ + very_long_argument_name2=-very.long.value.for_the_argument, + **kwargs, + ) +- +- + def g(): + "Docstring." +- + def inner(): + pass +- + print("Inner defs should breathe a little.") +- +- + def h(): + def inner(): + pass +- + print("Inner defs should breathe a little.") + + + if os.name == "posix": + import termios +- + def i_should_be_followed_by_only_one_newline(): + pass +- + elif os.name == "nt": + try: + import msvcrt +- + def i_should_be_followed_by_only_one_newline(): + pass + +@@ -54,12 +44,10 @@ + class IHopeYouAreHavingALovelyDay: + def __call__(self): + print("i_should_be_followed_by_only_one_newline") +- + else: + + def foo(): + pass +- + + with hmm_but_this_should_get_two_preceding_newlines(): + pass ``` ## Ruff Output ```py def f( - a, - **kwargs, + a, + **kwargs, ) -> A: with cache_dir(): if something: - result = CliRunner().invoke( - black.main, - [str(src1), str(src2), "--diff", "--check"], + result = ( + CliRunner().invoke(black.main, [str(src1), str(src2), "--diff", "--check"]) ) limited.append(-limited.pop()) # negate top return A( @@ -97,34 +150,24 @@ def f( very_long_argument_name2=-very.long.value.for_the_argument, **kwargs, ) - - def g(): "Docstring." - def inner(): pass - print("Inner defs should breathe a little.") - - def h(): def inner(): pass - print("Inner defs should breathe a little.") if os.name == "posix": import termios - def i_should_be_followed_by_only_one_newline(): pass - elif os.name == "nt": try: import msvcrt - def i_should_be_followed_by_only_one_newline(): pass @@ -138,13 +181,11 @@ elif False: class IHopeYouAreHavingALovelyDay: def __call__(self): print("i_should_be_followed_by_only_one_newline") - else: def foo(): pass - with hmm_but_this_should_get_two_preceding_newlines(): pass ``` 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 aacde3d87b9be..d2f7810be3811 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 @@ -108,24 +108,101 @@ def __await__(): return (yield) ```diff --- Black +++ Ruff -@@ -6,7 +6,7 @@ +@@ -4,97 +4,52 @@ - from library import some_connection, some_decorator + from third_party import X, Y, Z +-from library import some_connection, some_decorator +- -f"trigger 3.6 mode" +- +- ++from library import some_connection, \ ++ some_decorator +f'trigger 3.6 mode' - - def func_no_args(): -@@ -64,19 +64,15 @@ - - - def spaces2(result=_core.Value(None)): +- a +- b +- c +- if True: +- raise RuntimeError +- if False: +- ... +- for i in range(10): +- print(i) +- continue +- exec("new-style exec", {}, {}) +- return None +- +- ++ a; b; c ++ if True: raise RuntimeError ++ if False: ... ++ for i in range(10): ++ print(i) ++ continue ++ exec("new-style exec", {}, {}) ++ return None + async def coroutine(arg, exec=False): +- "Single-line docstring. Multiline is harder to reformat." +- async with some_connection() as conn: +- await conn.do_what_i_mean("SELECT bobby, tables FROM xkcd", timeout=2) +- await asyncio.sleep(1) +- +- ++ "Single-line docstring. Multiline is harder to reformat." ++ async with some_connection() as conn: ++ await conn.do_what_i_mean('SELECT bobby, tables FROM xkcd', timeout=2) ++ await asyncio.sleep(1) + @asyncio.coroutine +-@some_decorator(with_args=True, many_args=[1, 2, 3]) +-def function_signature_stress_test( +- number: int, +- no_annotation=None, +- text: str = "default", +- *, +- debug: bool = False, +- **kwargs, +-) -> str: +- return text[number:-1] +- +- +-def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r""): +- offset = attr.ib(default=attr.Factory(lambda: _r.uniform(10000, 200000))) +- assert task._cancel_stack[: len(old_stack)] == old_stack +- +- +-def spaces_types( +- a: int = 1, +- b: tuple = (), +- c: list = [], +- d: dict = {}, +- e: bool = True, +- f: int = -1, +- g: int = 1 if False else 2, +- h: str = "", +- i: str = r"", +-): +- ... +- +- +-def spaces2(result=_core.Value(None)): - assert fut is self._read_fut, (fut, self._read_fut) -+ assert fut is self._read_fut, fut, self._read_fut -+ - - +- +- ++@some_decorator( ++with_args=True, ++many_args=[1,2,3] ++) ++def function_signature_stress_test(number:int,no_annotation=None,text:str="default",* ,debug:bool=False,**kwargs) -> str: ++ return text[number:-1] ++def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r''): ++ offset = attr.ib(default=attr.Factory( lambda: _r.uniform(10000, 200000))) ++ assert task._cancel_stack[:len(old_stack)] == old_stack ++def spaces_types(a: int = 1, b: tuple = (), c: list = [], d: dict = {}, e: bool = True, f: int = -1, g: int = 1 if False else 2, h: str = "", i: str = r''): ... ++def spaces2(result= _core.Value(None)): ++ assert fut is self._read_fut, (fut, self._read_fut) ++ def example(session): - result = ( - session.query(models.Customer.id) @@ -136,30 +213,76 @@ def __await__(): return (yield) - .order_by(models.Customer.id.asc()) - .all() - ) +- +- + result = session.query(models.Customer.id).filter( + models.Customer.account_id == account_id, + models.Customer.email == email_address, -+ ).order_by(models.Customer.id.asc()).all() - - ++ ).order_by( ++ models.Customer.id.asc() ++ ).all() def long_lines(): -@@ -135,14 +131,13 @@ - a, - **kwargs, + if True: + typedargslist.extend( +- gen_annotated_params( +- ast_args.kwonlyargs, +- ast_args.kw_defaults, +- parameters, +- implicit_default=True, +- ) ++ gen_annotated_params(ast_args.kwonlyargs, ast_args.kw_defaults, parameters, implicit_default=True) + ) + typedargslist.extend( + gen_annotated_params( +- ast_args.kwonlyargs, +- ast_args.kw_defaults, +- parameters, +- implicit_default=True, ++ ast_args.kwonlyargs, ast_args.kw_defaults, parameters, implicit_default=True, + # trailing standalone comment + ) + ) +@@ -117,23 +72,18 @@ + \n? + ) + $ +- """, +- re.MULTILINE | re.VERBOSE, ++ """, re.MULTILINE | re.VERBOSE + ) +- +- + def trailing_comma(): + mapping = { +- A: 0.25 * (10.0 / 12), +- B: 0.1 * (10.0 / 12), +- C: 0.1 * (10.0 / 12), +- D: 0.1 * (10.0 / 12), +- } +- +- ++ A: 0.25 * (10.0 / 12), ++ B: 0.1 * (10.0 / 12), ++ C: 0.1 * (10.0 / 12), ++ D: 0.1 * (10.0 / 12), ++} + def f( +- a, +- **kwargs, ++ a, ++ **kwargs, ) -> A: -- return ( -+ return + return ( yield from A( - very_long_argument_name1=very_long_value_for_the_argument, - very_long_argument_name2=very_long_value_for_the_argument, +@@ -142,7 +92,4 @@ **kwargs, ) -- ) - - - def __await__(): + ) +- +- +-def __await__(): - return (yield) -+ return yield ++def __await__(): return (yield) ``` ## Ruff Output @@ -171,93 +294,52 @@ import sys from third_party import X, Y, Z -from library import some_connection, some_decorator - +from library import some_connection, \ + some_decorator f'trigger 3.6 mode' - - def func_no_args(): - a - b - c - if True: - raise RuntimeError - if False: - ... - for i in range(10): - print(i) - continue - exec("new-style exec", {}, {}) - return None - - + a; b; c + if True: raise RuntimeError + if False: ... + for i in range(10): + print(i) + continue + exec("new-style exec", {}, {}) + return None async def coroutine(arg, exec=False): - "Single-line docstring. Multiline is harder to reformat." - async with some_connection() as conn: - await conn.do_what_i_mean("SELECT bobby, tables FROM xkcd", timeout=2) - await asyncio.sleep(1) - - + "Single-line docstring. Multiline is harder to reformat." + async with some_connection() as conn: + await conn.do_what_i_mean('SELECT bobby, tables FROM xkcd', timeout=2) + await asyncio.sleep(1) @asyncio.coroutine -@some_decorator(with_args=True, many_args=[1, 2, 3]) -def function_signature_stress_test( - number: int, - no_annotation=None, - text: str = "default", - *, - debug: bool = False, - **kwargs, -) -> str: - return text[number:-1] - - -def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r""): - offset = attr.ib(default=attr.Factory(lambda: _r.uniform(10000, 200000))) - assert task._cancel_stack[: len(old_stack)] == old_stack - - -def spaces_types( - a: int = 1, - b: tuple = (), - c: list = [], - d: dict = {}, - e: bool = True, - f: int = -1, - g: int = 1 if False else 2, - h: str = "", - i: str = r"", -): - ... - - -def spaces2(result=_core.Value(None)): - assert fut is self._read_fut, fut, self._read_fut - - - +@some_decorator( +with_args=True, +many_args=[1,2,3] +) +def function_signature_stress_test(number:int,no_annotation=None,text:str="default",* ,debug:bool=False,**kwargs) -> str: + return text[number:-1] +def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r''): + offset = attr.ib(default=attr.Factory( lambda: _r.uniform(10000, 200000))) + assert task._cancel_stack[:len(old_stack)] == old_stack +def spaces_types(a: int = 1, b: tuple = (), c: list = [], d: dict = {}, e: bool = True, f: int = -1, g: int = 1 if False else 2, h: str = "", i: str = r''): ... +def spaces2(result= _core.Value(None)): + assert fut is self._read_fut, (fut, self._read_fut) + def example(session): result = session.query(models.Customer.id).filter( models.Customer.account_id == account_id, models.Customer.email == email_address, - ).order_by(models.Customer.id.asc()).all() - - + ).order_by( + models.Customer.id.asc() + ).all() def long_lines(): if True: typedargslist.extend( - gen_annotated_params( - ast_args.kwonlyargs, - ast_args.kw_defaults, - parameters, - implicit_default=True, - ) + gen_annotated_params(ast_args.kwonlyargs, ast_args.kw_defaults, parameters, implicit_default=True) ) typedargslist.extend( gen_annotated_params( - ast_args.kwonlyargs, - ast_args.kw_defaults, - parameters, - implicit_default=True, + ast_args.kwonlyargs, ast_args.kw_defaults, parameters, implicit_default=True, # trailing standalone comment ) ) @@ -280,34 +362,27 @@ def long_lines(): \n? ) $ - """, - re.MULTILINE | re.VERBOSE, + """, re.MULTILINE | re.VERBOSE ) - - def trailing_comma(): mapping = { - A: 0.25 * (10.0 / 12), - B: 0.1 * (10.0 / 12), - C: 0.1 * (10.0 / 12), - D: 0.1 * (10.0 / 12), - } - - + A: 0.25 * (10.0 / 12), + B: 0.1 * (10.0 / 12), + C: 0.1 * (10.0 / 12), + D: 0.1 * (10.0 / 12), +} def f( - a, - **kwargs, + a, + **kwargs, ) -> A: - return + return ( yield from A( very_long_argument_name1=very_long_value_for_the_argument, very_long_argument_name2=very_long_value_for_the_argument, **kwargs, ) - - -def __await__(): - return yield + ) +def __await__(): return (yield) ``` ## Black Output 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 b1b1d3dab322c..632989be77f81 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 @@ -74,195 +74,199 @@ some_module.some_function( ```diff --- Black +++ Ruff -@@ -52,53 +52,52 @@ - pass +@@ -1,69 +1,26 @@ +-def f( +- a, +-): +- d = { +- "key": "value", +- } ++def f(a,): ++ d = {'key': 'value',} + tup = (1,) +- +-def f2( +- a, +- b, +-): +- d = { +- "key": "value", +- "key2": "value2", +- } +- tup = ( +- 1, +- 2, +- ) +- ++def f2(a,b,): ++ d = {'key': 'value', 'key2': 'value2',} ++ tup = (1,2,) --def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( -- Set["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] +-def f( +- a: int = 1, -): +- call( +- arg={ +- "explode": "this", +- } +- ) +- call2( +- arg=[1, 2, 3], +- ) ++def f(a:int=1,): ++ call(arg={'explode': 'this',}) ++ call2(arg=[1,2,3],) + x = { + "a": 1, + "b": 2, + }["a"] +- if ( +- a +- == { +- "a": 1, +- "b": 2, +- "c": 3, +- "d": 4, +- "e": 5, +- "f": 6, +- "g": 7, +- "h": 8, +- }["a"] +- ): ++ if a == {"a": 1,"b": 2,"c": 3,"d": 4,"e": 5,"f": 6,"g": 7,"h": 8,}["a"]: + pass + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +]: - json = { - "k": { - "k2": { - "k3": [ - 1, ++ json = {"k": {"k2": {"k3": [1,]}}} + +-def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( +- Set["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] +-): +- json = { +- "k": { +- "k2": { +- "k3": [ +- 1, - ] - } - } -+ ], -+ }, -+ }, - } +- } # The type annotation shouldn't get a trailing comma since that would change its type. - # Relevant bug report: https://github.com/psf/black/issues/2381. --def some_function_with_a_really_long_name() -> ( -- returning_a_deeply_nested_import_of_a_type_i_suppose --): -+def some_function_with_a_really_long_name() -> returning_a_deeply_nested_import_of_a_type_i_suppose: +@@ -74,24 +31,21 @@ pass - def some_method_with_a_really_long_name( +-def some_method_with_a_really_long_name( - very_long_parameter_so_yeah: str, another_long_parameter: int -+ very_long_parameter_so_yeah: str, -+ another_long_parameter: int, - ) -> another_case_of_returning_a_deeply_nested_import_of_a_type_i_suppose_cause_why_not: +-) -> another_case_of_returning_a_deeply_nested_import_of_a_type_i_suppose_cause_why_not: ++def some_method_with_a_really_long_name(very_long_parameter_so_yeah: str, another_long_parameter: int) -> ( ++ another_case_of_returning_a_deeply_nested_import_of_a_type_i_suppose_cause_why_not ++): pass --def func() -> ( + def func() -> ( - also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black( - this_shouldn_t_get_a_trailing_comma_too - ) -+def func() -> also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black( -+ this_shouldn_t_get_a_trailing_comma_too ++ also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black(this_shouldn_t_get_a_trailing_comma_too) ): pass -def func() -> ( - also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black( -- this_shouldn_t_get_a_trailing_comma_too ++def func() -> ((also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black( + this_shouldn_t_get_a_trailing_comma_too - ) -+def func() -> also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black( -+ this_shouldn_t_get_a_trailing_comma_too ++ )) ): pass +@@ -103,12 +57,5 @@ - # Make sure inner one-element tuple won't explode + # Inner trailing comma causes outer to explode some_module.some_function( -- argument1, (one_element_tuple,), argument4, argument5, argument6 -+ argument1, -+ (one_element_tuple,), -+ argument4, -+ argument5, -+ argument6, +- argument1, +- ( +- one, +- two, +- ), +- argument4, +- argument5, +- argument6, ++ argument1, (one, two,), argument4, argument5, argument6 ) - - # Inner trailing comma causes outer to explode ``` ## Ruff Output ```py -def f( - a, -): - d = { - "key": "value", - } +def f(a,): + d = {'key': 'value',} tup = (1,) +def f2(a,b,): + d = {'key': 'value', 'key2': 'value2',} + tup = (1,2,) -def f2( - a, - b, -): - d = { - "key": "value", - "key2": "value2", - } - tup = ( - 1, - 2, - ) - - -def f( - a: int = 1, -): - call( - arg={ - "explode": "this", - } - ) - call2( - arg=[1, 2, 3], - ) +def f(a:int=1,): + call(arg={'explode': 'this',}) + call2(arg=[1,2,3],) x = { "a": 1, "b": 2, }["a"] - if ( - a - == { - "a": 1, - "b": 2, - "c": 3, - "d": 4, - "e": 5, - "f": 6, - "g": 7, - "h": 8, - }["a"] - ): + if a == {"a": 1,"b": 2,"c": 3,"d": 4,"e": 5,"f": 6,"g": 7,"h": 8,}["a"]: pass - def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ]: - json = { - "k": { - "k2": { - "k3": [ - 1, - ], - }, - }, - } + json = {"k": {"k2": {"k3": [1,]}}} + # The type annotation shouldn't get a trailing comma since that would change its type. # Relevant bug report: https://github.com/psf/black/issues/2381. -def some_function_with_a_really_long_name() -> returning_a_deeply_nested_import_of_a_type_i_suppose: +def some_function_with_a_really_long_name() -> ( + returning_a_deeply_nested_import_of_a_type_i_suppose +): pass -def some_method_with_a_really_long_name( - very_long_parameter_so_yeah: str, - another_long_parameter: int, -) -> another_case_of_returning_a_deeply_nested_import_of_a_type_i_suppose_cause_why_not: +def some_method_with_a_really_long_name(very_long_parameter_so_yeah: str, another_long_parameter: int) -> ( + another_case_of_returning_a_deeply_nested_import_of_a_type_i_suppose_cause_why_not +): pass -def func() -> also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black( - this_shouldn_t_get_a_trailing_comma_too +def func() -> ( + also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black(this_shouldn_t_get_a_trailing_comma_too) ): pass -def func() -> also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black( - this_shouldn_t_get_a_trailing_comma_too +def func() -> ((also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black( + this_shouldn_t_get_a_trailing_comma_too + )) ): pass # Make sure inner one-element tuple won't explode some_module.some_function( - argument1, - (one_element_tuple,), - argument4, - argument5, - argument6, + argument1, (one_element_tuple,), argument4, argument5, argument6 ) # Inner trailing comma causes outer to explode some_module.some_function( - argument1, - ( - one, - two, - ), - argument4, - argument5, - argument6, + argument1, (one, two,), argument4, argument5, argument6 ) ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__import_spacing_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__import_spacing_py.snap new file mode 100644 index 0000000000000..13eab2b6d7826 --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__import_spacing_py.snap @@ -0,0 +1,242 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/import_spacing.py +--- +## Input + +```py +"""The asyncio package, tracking PEP 3156.""" + +# flake8: noqa + +from logging import ( + WARNING +) +from logging import ( + ERROR, +) +import sys + +# This relies on each of the submodules having an __all__ variable. +from .base_events import * +from .coroutines import * +from .events import * # comment here + +from .futures import * +from .locks import * # comment here +from .protocols import * + +from ..runners import * # comment here +from ..queues import * +from ..streams import * + +from some_library import ( + Just, Enough, Libraries, To, Fit, In, This, Nice, Split, Which, We, No, Longer, Use +) +from name_of_a_company.extremely_long_project_name.component.ttypes import CuteLittleServiceHandlerFactoryyy +from name_of_a_company.extremely_long_project_name.extremely_long_component_name.ttypes import * + +from .a.b.c.subprocess import * +from . import (tasks) +from . import (A, B, C) +from . import SomeVeryLongNameAndAllOfItsAdditionalLetters1, \ + SomeVeryLongNameAndAllOfItsAdditionalLetters2 + +__all__ = ( + base_events.__all__ + + coroutines.__all__ + + events.__all__ + + futures.__all__ + + locks.__all__ + + protocols.__all__ + + runners.__all__ + + queues.__all__ + + streams.__all__ + + tasks.__all__ +) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -2,8 +2,10 @@ + + # flake8: noqa + +-from logging import WARNING + from logging import ( ++ WARNING ++) ++from logging import ( + ERROR, + ) + import sys +@@ -22,33 +24,16 @@ + from ..streams import * + + from some_library import ( +- Just, +- Enough, +- Libraries, +- To, +- Fit, +- In, +- This, +- Nice, +- Split, +- Which, +- We, +- No, +- Longer, +- Use, ++ Just, Enough, Libraries, To, Fit, In, This, Nice, Split, Which, We, No, Longer, Use + ) +-from name_of_a_company.extremely_long_project_name.component.ttypes import ( +- CuteLittleServiceHandlerFactoryyy, +-) ++from name_of_a_company.extremely_long_project_name.component.ttypes import CuteLittleServiceHandlerFactoryyy + from name_of_a_company.extremely_long_project_name.extremely_long_component_name.ttypes import * + + from .a.b.c.subprocess import * +-from . import tasks +-from . import A, B, C +-from . import ( +- SomeVeryLongNameAndAllOfItsAdditionalLetters1, +- SomeVeryLongNameAndAllOfItsAdditionalLetters2, +-) ++from . import (tasks) ++from . import (A, B, C) ++from . import SomeVeryLongNameAndAllOfItsAdditionalLetters1, \ ++ SomeVeryLongNameAndAllOfItsAdditionalLetters2 + + __all__ = ( + base_events.__all__ +``` + +## Ruff Output + +```py +"""The asyncio package, tracking PEP 3156.""" + +# flake8: noqa + +from logging import ( + WARNING +) +from logging import ( + ERROR, +) +import sys + +# This relies on each of the submodules having an __all__ variable. +from .base_events import * +from .coroutines import * +from .events import * # comment here + +from .futures import * +from .locks import * # comment here +from .protocols import * + +from ..runners import * # comment here +from ..queues import * +from ..streams import * + +from some_library import ( + Just, Enough, Libraries, To, Fit, In, This, Nice, Split, Which, We, No, Longer, Use +) +from name_of_a_company.extremely_long_project_name.component.ttypes import CuteLittleServiceHandlerFactoryyy +from name_of_a_company.extremely_long_project_name.extremely_long_component_name.ttypes import * + +from .a.b.c.subprocess import * +from . import (tasks) +from . import (A, B, C) +from . import SomeVeryLongNameAndAllOfItsAdditionalLetters1, \ + SomeVeryLongNameAndAllOfItsAdditionalLetters2 + +__all__ = ( + base_events.__all__ + + coroutines.__all__ + + events.__all__ + + futures.__all__ + + locks.__all__ + + protocols.__all__ + + runners.__all__ + + queues.__all__ + + streams.__all__ + + tasks.__all__ +) +``` + +## Black Output + +```py +"""The asyncio package, tracking PEP 3156.""" + +# flake8: noqa + +from logging import WARNING +from logging import ( + ERROR, +) +import sys + +# This relies on each of the submodules having an __all__ variable. +from .base_events import * +from .coroutines import * +from .events import * # comment here + +from .futures import * +from .locks import * # comment here +from .protocols import * + +from ..runners import * # comment here +from ..queues import * +from ..streams import * + +from some_library import ( + Just, + Enough, + Libraries, + To, + Fit, + In, + This, + Nice, + Split, + Which, + We, + No, + Longer, + Use, +) +from name_of_a_company.extremely_long_project_name.component.ttypes import ( + CuteLittleServiceHandlerFactoryyy, +) +from name_of_a_company.extremely_long_project_name.extremely_long_component_name.ttypes import * + +from .a.b.c.subprocess import * +from . import tasks +from . import A, B, C +from . import ( + SomeVeryLongNameAndAllOfItsAdditionalLetters1, + SomeVeryLongNameAndAllOfItsAdditionalLetters2, +) + +__all__ = ( + base_events.__all__ + + coroutines.__all__ + + events.__all__ + + futures.__all__ + + locks.__all__ + + protocols.__all__ + + runners.__all__ + + queues.__all__ + + streams.__all__ + + tasks.__all__ +) +``` + + 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 new file mode 100644 index 0000000000000..8b1a22c94f086 --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__one_element_subscript_py.snap @@ -0,0 +1,98 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/one_element_subscript.py +--- +## Input + +```py +# We should not treat the trailing comma +# in a single-element subscript. +a: tuple[int,] +b = tuple[int,] + +# The magic comma still applies to multi-element subscripts. +c: tuple[int, int,] +d = tuple[int, int,] + +# Magic commas still work as expected for non-subscripts. +small_list = [1,] +list_of_types = [tuple[int,],] +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -4,19 +4,9 @@ + b = tuple[int,] + + # The magic comma still applies to multi-element subscripts. +-c: tuple[ +- int, +- int, +-] +-d = tuple[ +- int, +- int, +-] ++c: tuple[int, int,] ++d = tuple[int, int,] + + # Magic commas still work as expected for non-subscripts. +-small_list = [ +- 1, +-] +-list_of_types = [ +- tuple[int,], +-] ++small_list = [1,] ++list_of_types = [tuple[int,],] +``` + +## Ruff Output + +```py +# We should not treat the trailing comma +# in a single-element subscript. +a: tuple[int,] +b = tuple[int,] + +# The magic comma still applies to multi-element subscripts. +c: tuple[int, int,] +d = tuple[int, int,] + +# Magic commas still work as expected for non-subscripts. +small_list = [1,] +list_of_types = [tuple[int,],] +``` + +## Black Output + +```py +# We should not treat the trailing comma +# in a single-element subscript. +a: tuple[int,] +b = tuple[int,] + +# The magic comma still applies to multi-element subscripts. +c: tuple[ + int, + int, +] +d = tuple[ + int, + int, +] + +# Magic commas still work as expected for non-subscripts. +small_list = [ + 1, +] +list_of_types = [ + tuple[int,], +] +``` + + 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 new file mode 100644 index 0000000000000..cc31431bdbb2f --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__power_op_spacing_py.snap @@ -0,0 +1,230 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/power_op_spacing.py +--- +## Input + +```py +def function(**kwargs): + t = a**2 + b**3 + return t ** 2 + + +def function_replace_spaces(**kwargs): + t = a **2 + b** 3 + c ** 4 + + +def function_dont_replace_spaces(): + {**a, **b, **c} + + +a = 5**~4 +b = 5 ** f() +c = -(5**2) +d = 5 ** f["hi"] +e = lazy(lambda **kwargs: 5) +f = f() ** 5 +g = a.b**c.d +h = 5 ** funcs.f() +i = funcs.f() ** 5 +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) +m = [([2**63], [1, 2**63])] +n = count <= 10**5 +o = settings(max_examples=10**6) +p = {(k, k**2): v**2 for k, v in pairs} +q = [10**i for i in range(6)] +r = x**y + +a = 5.0**~4.0 +b = 5.0 ** f() +c = -(5.0**2.0) +d = 5.0 ** f["hi"] +e = lazy(lambda **kwargs: 5) +f = f() ** 5.0 +g = a.b**c.d +h = 5.0 ** funcs.f() +i = funcs.f() ** 5.0 +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) +m = [([2.0**63.0], [1.0, 2**63.0])] +n = count <= 10**5.0 +o = settings(max_examples=10**6.0) +p = {(k, k**2): v**2.0 for k, v in pairs} +q = [10.5**i for i in range(6)] + + +# WE SHOULD DEFINITELY NOT EAT THESE COMMENTS (https://github.com/psf/black/issues/2873) +if hasattr(view, "sum_of_weights"): + return np.divide( # type: ignore[no-any-return] + view.variance, # type: ignore[union-attr] + view.sum_of_weights, # type: ignore[union-attr] + out=np.full(view.sum_of_weights.shape, np.nan), # type: ignore[union-attr] + where=view.sum_of_weights**2 > view.sum_of_weights_squared, # type: ignore[union-attr] + ) + +return np.divide( + where=view.sum_of_weights_of_weight_long**2 > view.sum_of_weights_squared, # type: ignore +) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,10 +1,10 @@ + def function(**kwargs): + t = a**2 + b**3 +- return t**2 ++ return t ** 2 + + + def function_replace_spaces(**kwargs): +- t = a**2 + b**3 + c**4 ++ t = a **2 + b** 3 + c ** 4 + + + def function_dont_replace_spaces(): +``` + +## Ruff Output + +```py +def function(**kwargs): + t = a**2 + b**3 + return t ** 2 + + +def function_replace_spaces(**kwargs): + t = a **2 + b** 3 + c ** 4 + + +def function_dont_replace_spaces(): + {**a, **b, **c} + + +a = 5**~4 +b = 5 ** f() +c = -(5**2) +d = 5 ** f["hi"] +e = lazy(lambda **kwargs: 5) +f = f() ** 5 +g = a.b**c.d +h = 5 ** funcs.f() +i = funcs.f() ** 5 +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) +m = [([2**63], [1, 2**63])] +n = count <= 10**5 +o = settings(max_examples=10**6) +p = {(k, k**2): v**2 for k, v in pairs} +q = [10**i for i in range(6)] +r = x**y + +a = 5.0**~4.0 +b = 5.0 ** f() +c = -(5.0**2.0) +d = 5.0 ** f["hi"] +e = lazy(lambda **kwargs: 5) +f = f() ** 5.0 +g = a.b**c.d +h = 5.0 ** funcs.f() +i = funcs.f() ** 5.0 +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) +m = [([2.0**63.0], [1.0, 2**63.0])] +n = count <= 10**5.0 +o = settings(max_examples=10**6.0) +p = {(k, k**2): v**2.0 for k, v in pairs} +q = [10.5**i for i in range(6)] + + +# WE SHOULD DEFINITELY NOT EAT THESE COMMENTS (https://github.com/psf/black/issues/2873) +if hasattr(view, "sum_of_weights"): + return np.divide( # type: ignore[no-any-return] + view.variance, # type: ignore[union-attr] + view.sum_of_weights, # type: ignore[union-attr] + out=np.full(view.sum_of_weights.shape, np.nan), # type: ignore[union-attr] + where=view.sum_of_weights**2 > view.sum_of_weights_squared, # type: ignore[union-attr] + ) + +return np.divide( + where=view.sum_of_weights_of_weight_long**2 > view.sum_of_weights_squared, # type: ignore +) +``` + +## Black Output + +```py +def function(**kwargs): + t = a**2 + b**3 + return t**2 + + +def function_replace_spaces(**kwargs): + t = a**2 + b**3 + c**4 + + +def function_dont_replace_spaces(): + {**a, **b, **c} + + +a = 5**~4 +b = 5 ** f() +c = -(5**2) +d = 5 ** f["hi"] +e = lazy(lambda **kwargs: 5) +f = f() ** 5 +g = a.b**c.d +h = 5 ** funcs.f() +i = funcs.f() ** 5 +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) +m = [([2**63], [1, 2**63])] +n = count <= 10**5 +o = settings(max_examples=10**6) +p = {(k, k**2): v**2 for k, v in pairs} +q = [10**i for i in range(6)] +r = x**y + +a = 5.0**~4.0 +b = 5.0 ** f() +c = -(5.0**2.0) +d = 5.0 ** f["hi"] +e = lazy(lambda **kwargs: 5) +f = f() ** 5.0 +g = a.b**c.d +h = 5.0 ** funcs.f() +i = funcs.f() ** 5.0 +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) +m = [([2.0**63.0], [1.0, 2**63.0])] +n = count <= 10**5.0 +o = settings(max_examples=10**6.0) +p = {(k, k**2): v**2.0 for k, v in pairs} +q = [10.5**i for i in range(6)] + + +# WE SHOULD DEFINITELY NOT EAT THESE COMMENTS (https://github.com/psf/black/issues/2873) +if hasattr(view, "sum_of_weights"): + return np.divide( # type: ignore[no-any-return] + view.variance, # type: ignore[union-attr] + view.sum_of_weights, # type: ignore[union-attr] + out=np.full(view.sum_of_weights.shape, np.nan), # type: ignore[union-attr] + where=view.sum_of_weights**2 > view.sum_of_weights_squared, # type: ignore[union-attr] + ) + +return np.divide( + where=view.sum_of_weights_of_weight_long**2 > view.sum_of_weights_squared, # type: ignore +) +``` + + 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 new file mode 100644 index 0000000000000..9f60197f9f4c1 --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__prefer_rhs_split_reformatted_py.snap @@ -0,0 +1,96 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/prefer_rhs_split_reformatted.py +--- +## Input + +```py +# Test cases separate from `prefer_rhs_split.py` that contains unformatted source. + +# Left hand side fits in a single line but will still be exploded by the +# magic trailing comma. +first_value, (m1, m2,), third_value = xxxxxx_yyyyyy_zzzzzz_wwwwww_uuuuuuu_vvvvvvvvvvv( + arg1, + arg2, +) + +# 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. +xxxxxxxxx_yyy_zzzzzzzz[xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1)] = 1 +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -2,20 +2,11 @@ + + # Left hand side fits in a single line but will still be exploded by the + # magic trailing comma. +-( +- first_value, +- ( +- m1, +- m2, +- ), +- third_value, +-) = xxxxxx_yyyyyy_zzzzzz_wwwwww_uuuuuuu_vvvvvvvvvvv( ++first_value, (m1, m2,), third_value = xxxxxx_yyyyyy_zzzzzz_wwwwww_uuuuuuu_vvvvvvvvvvv( + arg1, + arg2, + ) + + # 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. +-xxxxxxxxx_yyy_zzzzzzzz[ +- xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1) +-] = 1 ++xxxxxxxxx_yyy_zzzzzzzz[xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1)] = 1 +``` + +## Ruff Output + +```py +# Test cases separate from `prefer_rhs_split.py` that contains unformatted source. + +# Left hand side fits in a single line but will still be exploded by the +# magic trailing comma. +first_value, (m1, m2,), third_value = xxxxxx_yyyyyy_zzzzzz_wwwwww_uuuuuuu_vvvvvvvvvvv( + arg1, + arg2, +) + +# 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. +xxxxxxxxx_yyy_zzzzzzzz[xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1)] = 1 +``` + +## Black Output + +```py +# Test cases separate from `prefer_rhs_split.py` that contains unformatted source. + +# Left hand side fits in a single line but will still be exploded by the +# magic trailing comma. +( + first_value, + ( + m1, + m2, + ), + third_value, +) = xxxxxx_yyyyyy_zzzzzz_wwwwww_uuuuuuu_vvvvvvvvvvv( + arg1, + arg2, +) + +# 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. +xxxxxxxxx_yyy_zzzzzzzz[ + xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1) +] = 1 +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_await_parens_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_await_parens_py.snap index 644787aaabd06..be33aaae3381a 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_await_parens_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_await_parens_py.snap @@ -94,39 +94,55 @@ async def main(): ```diff --- Black +++ Ruff -@@ -8,59 +8,63 @@ +@@ -1,93 +1,81 @@ + import asyncio + +- + # Control example + async def main(): + await asyncio.sleep(1) +- # Remove brackets for short coroutine/task async def main(): - await asyncio.sleep(1) +- + await (asyncio.sleep(1)) - async def main(): - await asyncio.sleep(1) -+ await (asyncio.sleep(1)) - +- ++ await ( ++ asyncio.sleep(1) ++ ) async def main(): - await asyncio.sleep(1) -+ await (asyncio.sleep(1)) - +- ++ await (asyncio.sleep(1) ++ ) # Check comments async def main(): - await asyncio.sleep(1) # Hello -+ await (asyncio.sleep(1)) - +- ++ await ( # Hello ++ asyncio.sleep(1) ++ ) async def main(): - await asyncio.sleep(1) # Hello -+ await (asyncio.sleep(1)) # Hello - +- ++ await ( ++ asyncio.sleep(1) # Hello ++ ) async def main(): - await asyncio.sleep(1) # Hello -+ await (asyncio.sleep(1)) # Hello - +- ++ await ( ++ asyncio.sleep(1) ++ ) # Hello # Long lines async def main(): @@ -138,18 +154,9 @@ async def main(): - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), -+ ( -+ await asyncio.gather( -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ ) - ) - +- ) +- ++ await asyncio.gather(asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1)) # Same as above but with magic trailing comma in function async def main(): @@ -161,40 +168,58 @@ async def main(): - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), -+ ( -+ await asyncio.gather( -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ asyncio.sleep(1), -+ ) - ) - +- ) +- ++ await asyncio.gather(asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1),) # Cr@zY Br@ck3Tz async def main(): - await black(1) -+ await (black(1)) - +- ++ await ( ++ ((((((((((((( ++ ((( ((( ++ ((( ((( ++ ((( ((( ++ ((( ((( ++ ((black(1))) ++ ))) ))) ++ ))) ))) ++ ))) ))) ++ ))) ))) ++ ))))))))))))) ++ ) # Keep brackets around non power operations and nested awaits -@@ -82,11 +86,11 @@ + async def main(): + await (set_of_tasks | other_set) +- + async def main(): + await (await asyncio.sleep(1)) + +- + # It's awaits all the way down... + async def main(): + await (await x) +- + + async def main(): + await (yield x) +- async def main(): - await (await asyncio.sleep(1)) +- + await (await (asyncio.sleep(1))) - async def main(): - await (await (await (await (await asyncio.sleep(1))))) +- + await (await (await (await (await (asyncio.sleep(1)))))) - async def main(): + await (yield) ``` ## Ruff Output @@ -202,99 +227,83 @@ async def main(): ```py import asyncio - # Control example async def main(): await asyncio.sleep(1) - # Remove brackets for short coroutine/task async def main(): await (asyncio.sleep(1)) - async def main(): - await (asyncio.sleep(1)) - + await ( + asyncio.sleep(1) + ) async def main(): - await (asyncio.sleep(1)) - + await (asyncio.sleep(1) + ) # Check comments async def main(): - await (asyncio.sleep(1)) - + await ( # Hello + asyncio.sleep(1) + ) async def main(): - await (asyncio.sleep(1)) # Hello - + await ( + asyncio.sleep(1) # Hello + ) async def main(): - await (asyncio.sleep(1)) # Hello - + await ( + asyncio.sleep(1) + ) # Hello # Long lines async def main(): - ( - await asyncio.gather( - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - ) - ) - + await asyncio.gather(asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1)) # Same as above but with magic trailing comma in function async def main(): - ( - await asyncio.gather( - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - asyncio.sleep(1), - ) - ) - + await asyncio.gather(asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1), asyncio.sleep(1),) # Cr@zY Br@ck3Tz async def main(): - await (black(1)) - + await ( + ((((((((((((( + ((( ((( + ((( ((( + ((( ((( + ((( ((( + ((black(1))) + ))) ))) + ))) ))) + ))) ))) + ))) ))) + ))))))))))))) + ) # Keep brackets around non power operations and nested awaits async def main(): await (set_of_tasks | other_set) - async def main(): await (await asyncio.sleep(1)) - # It's awaits all the way down... async def main(): await (await x) - async def main(): await (yield x) - async def main(): await (await (asyncio.sleep(1))) - async def main(): await (await (await (await (await (asyncio.sleep(1)))))) - async def main(): await (yield) ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_except_parens_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_except_parens_py.snap index 2d8aa71163539..8f02ffe3f18f4 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_except_parens_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_except_parens_py.snap @@ -57,7 +57,7 @@ except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.ov raise err # This is tuple of exceptions. -@@ -21,9 +21,7 @@ +@@ -21,22 +21,15 @@ # Test long variants. try: a.something @@ -68,6 +68,21 @@ except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.ov raise err try: + a.something +-except ( +- some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error, +-) as err: ++except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error,) as err: + raise err + + try: + a.something +-except ( +- some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error, +- some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error, +-) as err: ++except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error, some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error) as err: + raise err ``` ## Ruff Output @@ -101,17 +116,12 @@ except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.ov try: a.something -except ( - some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error, -) as err: +except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error,) as err: raise err try: a.something -except ( - some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error, - some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error, -) as err: +except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error, some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error) as err: raise err ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_for_brackets_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_for_brackets_py.snap index 544ca7ed1fff6..7f0c038d1f94c 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_for_brackets_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_for_brackets_py.snap @@ -32,24 +32,44 @@ for (((((k, v))))) in d.items(): ```diff --- Black +++ Ruff -@@ -17,9 +17,7 @@ - for ( - k, - v, +@@ -1,5 +1,5 @@ + # Only remove tuple brackets after `for` +-for k, v in d.items(): ++for (k, v) in d.items(): + print(k, v) + + # Don't touch tuple brackets after `in` +@@ -8,20 +8,12 @@ + module._verify_python3_env = lambda: None + + # Brackets remain for long for loop lines +-for ( +- why_would_anyone_choose_to_name_a_loop_variable_with_a_name_this_long, +- i_dont_know_but_we_should_still_check_the_behaviour_if_they_do, +-) in d.items(): ++for (why_would_anyone_choose_to_name_a_loop_variable_with_a_name_this_long, i_dont_know_but_we_should_still_check_the_behaviour_if_they_do) in d.items(): + print(k, v) + +-for ( +- k, +- v, -) in ( - dfkasdjfldsjflkdsjflkdsjfdslkfjldsjfgkjdshgkljjdsfldgkhsdofudsfudsofajdslkfjdslkfjldisfjdffjsdlkfjdlkjjkdflskadjldkfjsalkfjdasj.items() -): -+) in dfkasdjfldsjflkdsjflkdsjfdslkfjldsjfgkjdshgkljjdsfldgkhsdofudsfudsofajdslkfjdslkfjldisfjdffjsdlkfjdlkjjkdflskadjldkfjsalkfjdasj.items(): ++for (k, v) in dfkasdjfldsjflkdsjflkdsjfdslkfjldsjfgkjdshgkljjdsfldgkhsdofudsfudsofajdslkfjdslkfjldisfjdffjsdlkfjdlkjjkdflskadjldkfjsalkfjdasj.items(): print(k, v) # Test deeply nested brackets +-for k, v in d.items(): ++for (((((k, v))))) in d.items(): + print(k, v) ``` ## Ruff Output ```py # Only remove tuple brackets after `for` -for k, v in d.items(): +for (k, v) in d.items(): print(k, v) # Don't touch tuple brackets after `in` @@ -58,20 +78,14 @@ for module in (core, _unicodefun): module._verify_python3_env = lambda: None # Brackets remain for long for loop lines -for ( - why_would_anyone_choose_to_name_a_loop_variable_with_a_name_this_long, - i_dont_know_but_we_should_still_check_the_behaviour_if_they_do, -) in d.items(): +for (why_would_anyone_choose_to_name_a_loop_variable_with_a_name_this_long, i_dont_know_but_we_should_still_check_the_behaviour_if_they_do) in d.items(): print(k, v) -for ( - k, - v, -) in dfkasdjfldsjflkdsjflkdsjfdslkfjldsjfgkjdshgkljjdsfldgkhsdofudsfudsofajdslkfjdslkfjldisfjdffjsdlkfjdlkjjkdflskadjldkfjsalkfjdasj.items(): +for (k, v) in dfkasdjfldsjflkdsjflkdsjfdslkfjldsjfgkjdshgkljjdsfldgkhsdofudsfudsofajdslkfjdslkfjldisfjdffjsdlkfjdlkjjkdflskadjldkfjsalkfjdasj.items(): print(k, v) # Test deeply nested brackets -for k, v in d.items(): +for (((((k, v))))) in d.items(): print(k, v) ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_newline_after_code_block_open_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_newline_after_code_block_open_py.snap new file mode 100644 index 0000000000000..f632858d07357 --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_newline_after_code_block_open_py.snap @@ -0,0 +1,428 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_newline_after_code_block_open.py +--- +## Input + +```py +import random + + +def foo1(): + + print("The newline above me should be deleted!") + + +def foo2(): + + + + print("All the newlines above me should be deleted!") + + +def foo3(): + + print("No newline above me!") + + print("There is a newline above me, and that's OK!") + + +def foo4(): + + # There is a comment here + + print("The newline above me should not be deleted!") + + +class Foo: + def bar(self): + + print("The newline above me should be deleted!") + + +for i in range(5): + + print(f"{i}) The line above me should be removed!") + + +for i in range(5): + + + + print(f"{i}) The lines above me should be removed!") + + +for i in range(5): + + for j in range(7): + + print(f"{i}) The lines above me should be removed!") + + +if random.randint(0, 3) == 0: + + print("The new line above me is about to be removed!") + + +if random.randint(0, 3) == 0: + + + + + print("The new lines above me is about to be removed!") + + +if random.randint(0, 3) == 0: + if random.uniform(0, 1) > 0.5: + print("Two lines above me are about to be removed!") + + +while True: + + print("The newline above me should be deleted!") + + +while True: + + + + print("The newlines above me should be deleted!") + + +while True: + + while False: + + print("The newlines above me should be deleted!") + + +with open("/path/to/file.txt", mode="w") as file: + + file.write("The new line above me is about to be removed!") + + +with open("/path/to/file.txt", mode="w") as file: + + + + file.write("The new lines above me is about to be removed!") + + +with open("/path/to/file.txt", mode="r") as read_file: + + with open("/path/to/output_file.txt", mode="w") as write_file: + + write_file.writelines(read_file.readlines()) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -2,20 +2,26 @@ + + + def foo1(): ++ + print("The newline above me should be deleted!") + + + def foo2(): ++ ++ ++ + print("All the newlines above me should be deleted!") + + + def foo3(): ++ + print("No newline above me!") + + print("There is a newline above me, and that's OK!") + + + def foo4(): ++ + # There is a comment here + + print("The newline above me should not be deleted!") +@@ -23,27 +29,39 @@ + + class Foo: + def bar(self): ++ + print("The newline above me should be deleted!") + + + for i in range(5): ++ + print(f"{i}) The line above me should be removed!") + + + for i in range(5): ++ ++ ++ + print(f"{i}) The lines above me should be removed!") + + + for i in range(5): ++ + for j in range(7): ++ + print(f"{i}) The lines above me should be removed!") + + + if random.randint(0, 3) == 0: ++ + print("The new line above me is about to be removed!") + + + if random.randint(0, 3) == 0: ++ ++ ++ ++ + print("The new lines above me is about to be removed!") + + +@@ -53,26 +71,38 @@ + + + while True: ++ + print("The newline above me should be deleted!") + + + while True: ++ ++ ++ + print("The newlines above me should be deleted!") + + + while True: ++ + while False: ++ + print("The newlines above me should be deleted!") + + + with open("/path/to/file.txt", mode="w") as file: ++ + file.write("The new line above me is about to be removed!") + + + with open("/path/to/file.txt", mode="w") as file: ++ ++ ++ + file.write("The new lines above me is about to be removed!") + + + with open("/path/to/file.txt", mode="r") as read_file: ++ + with open("/path/to/output_file.txt", mode="w") as write_file: ++ + write_file.writelines(read_file.readlines()) +``` + +## Ruff Output + +```py +import random + + +def foo1(): + + print("The newline above me should be deleted!") + + +def foo2(): + + + + print("All the newlines above me should be deleted!") + + +def foo3(): + + print("No newline above me!") + + print("There is a newline above me, and that's OK!") + + +def foo4(): + + # There is a comment here + + print("The newline above me should not be deleted!") + + +class Foo: + def bar(self): + + print("The newline above me should be deleted!") + + +for i in range(5): + + print(f"{i}) The line above me should be removed!") + + +for i in range(5): + + + + print(f"{i}) The lines above me should be removed!") + + +for i in range(5): + + for j in range(7): + + print(f"{i}) The lines above me should be removed!") + + +if random.randint(0, 3) == 0: + + print("The new line above me is about to be removed!") + + +if random.randint(0, 3) == 0: + + + + + print("The new lines above me is about to be removed!") + + +if random.randint(0, 3) == 0: + if random.uniform(0, 1) > 0.5: + print("Two lines above me are about to be removed!") + + +while True: + + print("The newline above me should be deleted!") + + +while True: + + + + print("The newlines above me should be deleted!") + + +while True: + + while False: + + print("The newlines above me should be deleted!") + + +with open("/path/to/file.txt", mode="w") as file: + + file.write("The new line above me is about to be removed!") + + +with open("/path/to/file.txt", mode="w") as file: + + + + file.write("The new lines above me is about to be removed!") + + +with open("/path/to/file.txt", mode="r") as read_file: + + with open("/path/to/output_file.txt", mode="w") as write_file: + + write_file.writelines(read_file.readlines()) +``` + +## Black Output + +```py +import random + + +def foo1(): + print("The newline above me should be deleted!") + + +def foo2(): + print("All the newlines above me should be deleted!") + + +def foo3(): + print("No newline above me!") + + print("There is a newline above me, and that's OK!") + + +def foo4(): + # There is a comment here + + print("The newline above me should not be deleted!") + + +class Foo: + def bar(self): + print("The newline above me should be deleted!") + + +for i in range(5): + print(f"{i}) The line above me should be removed!") + + +for i in range(5): + print(f"{i}) The lines above me should be removed!") + + +for i in range(5): + for j in range(7): + print(f"{i}) The lines above me should be removed!") + + +if random.randint(0, 3) == 0: + print("The new line above me is about to be removed!") + + +if random.randint(0, 3) == 0: + print("The new lines above me is about to be removed!") + + +if random.randint(0, 3) == 0: + if random.uniform(0, 1) > 0.5: + print("Two lines above me are about to be removed!") + + +while True: + print("The newline above me should be deleted!") + + +while True: + print("The newlines above me should be deleted!") + + +while True: + while False: + print("The newlines above me should be deleted!") + + +with open("/path/to/file.txt", mode="w") as file: + file.write("The new line above me is about to be removed!") + + +with open("/path/to/file.txt", mode="w") as file: + file.write("The new lines above me is about to be removed!") + + +with open("/path/to/file.txt", mode="r") as read_file: + with open("/path/to/output_file.txt", mode="w") as write_file: + write_file.writelines(read_file.readlines()) +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_parens_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_parens_py.snap index b17e75d81dcef..bb41990ddafc9 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_parens_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_parens_py.snap @@ -68,127 +68,166 @@ def example8(): ```diff --- Black +++ Ruff -@@ -1,18 +1,14 @@ - x = 1 - x = 1.2 - --data = ( -- "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" --).encode() -+data = ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx").encode() +@@ -1,85 +1,55 @@ +-x = 1 +-x = 1.2 ++x = (1) ++x = (1.2) + data = ( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + ).encode() +- async def show_status(): while True: try: if report_host: -- data = ( + data = ( - f"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - ).encode() -+ data = (f"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx").encode() ++ f"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ++ ).encode() except Exception as e: pass +- + + def example(): +- return "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ++ return (("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")) + + + def example1(): +- return 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111 ++ return ((1111111111111111111111111111111111111111111111111111111111111111111111111111111111111)) + -@@ -30,15 +26,11 @@ + def example1point5(): +- return 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111 ++ return ((((((1111111111111111111111111111111111111111111111111111111111111111111111111111111111111)))))) def example2(): - return ( - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - ) -+ return "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ++ return (("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")) def example3(): - return ( - 1111111111111111111111111111111111111111111111111111111111111111111111111111111 - ) -+ return 1111111111111111111111111111111111111111111111111111111111111111111111111111111 ++ return ((1111111111111111111111111111111111111111111111111111111111111111111111111111111)) def example4(): +- return True ++ return ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((True)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) + + + def example5(): +- return () ++ return ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) + + + def example6(): +- return {a: a for a in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]} ++ return ((((((((({a:a for a in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]}))))))))) + + + def example7(): +- return { +- a: a +- for a in [ +- 1, +- 2, +- 3, +- 4, +- 5, +- 6, +- 7, +- 8, +- 9, +- 10, +- 11, +- 12, +- 13, +- 14, +- 15, +- 16, +- 17, +- 18, +- 19, +- 20000000000000000000, +- ] +- } ++ return ((((((((({a:a for a in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20000000000000000000]}))))))))) + + + def example8(): +- return None ++ return (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((None))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) ``` ## Ruff Output ```py -x = 1 -x = 1.2 - -data = ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx").encode() +x = (1) +x = (1.2) +data = ( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +).encode() async def show_status(): while True: try: if report_host: - data = (f"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx").encode() + data = ( + f"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + ).encode() except Exception as e: pass - def example(): - return "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + return (("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")) def example1(): - return 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111 + return ((1111111111111111111111111111111111111111111111111111111111111111111111111111111111111)) def example1point5(): - return 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111 + return ((((((1111111111111111111111111111111111111111111111111111111111111111111111111111111111111)))))) def example2(): - return "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + return (("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")) def example3(): - return 1111111111111111111111111111111111111111111111111111111111111111111111111111111 + return ((1111111111111111111111111111111111111111111111111111111111111111111111111111111)) def example4(): - return True + return ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((True)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) def example5(): - return () + return ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) def example6(): - return {a: a for a in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]} + return ((((((((({a:a for a in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]}))))))))) def example7(): - return { - a: a - for a in [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20000000000000000000, - ] - } + return ((((((((({a:a for a in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20000000000000000000]}))))))))) def example8(): - return None + return (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((None))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) ``` ## 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 99dada1ae2f6e..c4f4e990330aa 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 @@ -101,131 +101,159 @@ def foo() -> tuple[int, int, int,]: ```diff --- Black +++ Ruff -@@ -1,52 +1,53 @@ +@@ -1,120 +1,88 @@ # Control def double(a: int) -> int: - return 2 * a -+ return 2 -+ * a - ++ return 2*a +- # Remove the brackets - def double(a: int) -> int: +-def double(a: int) -> int: - return 2 * a -+ return 2 -+ * a - +- ++def double(a: int) -> (int): ++ return 2*a # Some newline variations - def double(a: int) -> int: +-def double(a: int) -> int: - return 2 * a -+ return 2 -+ * a - ++def double(a: int) -> ( ++ int): ++ return 2*a - def double(a: int) -> int: +- +-def double(a: int) -> int: - return 2 * a -+ return 2 -+ * a - - - def double(a: int) -> int: +- +- +-def double(a: int) -> int: - return 2 * a -+ return 2 -+ * a ++def double(a: int) -> (int ++): ++ return 2*a ++def double(a: int) -> ( ++ int ++): ++ return 2*a # Don't lose the comments - def double(a: int) -> int: # Hello +-def double(a: int) -> int: # Hello - return 2 * a -+ return 2 -+ * a - +- ++def double(a: int) -> ( # Hello ++ int ++): ++ return 2*a -def double(a: int) -> int: # Hello - return 2 * a -+def double(a: int) -> int: -+ return 2 -+ * a - +- ++def double(a: int) -> ( ++ int # Hello ++): ++ return 2*a # Really long annotations --def foo() -> ( -- intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds --): -+def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: + def foo() -> ( + intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds + ): return 2 - +- -def foo() -> ( - intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds -): +def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: return 2 - +- -def foo() -> ( - intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds - | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds -): -+def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds -+| intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: ++def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: return 2 +- +-def foo( +- a: int, +- b: int, +- c: int, +-) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: ++def foo(a: int, b: int, c: int,) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: + return 2 -@@ -62,10 +63,8 @@ - a: int, - b: int, - c: int, +- +-def foo( +- a: int, +- b: int, +- c: int, -) -> ( - intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds - | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds -): -+) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds -+| intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: ++def foo(a: int, b: int, c: int,) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: return 2 +- + # Split args but no need to split return +-def foo( +- a: int, +- b: int, +- c: int, +-) -> int: ++def foo(a: int, b: int, c: int,) -> int: + return 2 -@@ -81,16 +80,16 @@ +- # Deeply nested brackets # with *interesting* spacing - def double(a: int) -> int: +-def double(a: int) -> int: - return 2 * a -+ return 2 -+ * a - - - def double(a: int) -> int: +- +- +-def double(a: int) -> int: - return 2 * a -+ return 2 -+ * a - - --def foo() -> ( -- intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds ++def double(a: int) -> (((((int))))): ++ return 2*a + ++def double(a: int) -> ( ++ ( ( ++ ((int) ++ ) ++ ) ++ ) ++ ): ++ return 2*a + + def foo() -> ( ++ ( ( + intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds -): -+def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: ++) ++)): return 2 - -@@ -99,22 +98,18 @@ - return 2 - - --def foo() -> ( +- + # Return type with commas +-def foo() -> tuple[int, int, int]: +- return 2 +- +- + def foo() -> ( - tuple[ - loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, - loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, - loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, - ] --): -+def foo() -> tuple[ -+ loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, -+ loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, -+ loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, -+]: ++ tuple[int, int, int] + ): return 2 ++def foo() -> tuple[loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong]: ++ return 2 # Magic trailing comma example -def foo() -> ( @@ -235,11 +263,7 @@ def foo() -> tuple[int, int, int,]: - int, - ] -): -+def foo() -> tuple[ -+ int, -+ int, -+ int, -+]: ++def foo() -> tuple[int, int, int,]: return 2 ``` @@ -248,118 +272,91 @@ def foo() -> tuple[int, int, int,]: ```py # Control def double(a: int) -> int: - return 2 - * a - + return 2*a # Remove the brackets -def double(a: int) -> int: - return 2 - * a - +def double(a: int) -> (int): + return 2*a # Some newline variations -def double(a: int) -> int: - return 2 - * a - - -def double(a: int) -> int: - return 2 - * a - +def double(a: int) -> ( + int): + return 2*a -def double(a: int) -> int: - return 2 - * a +def double(a: int) -> (int +): + return 2*a +def double(a: int) -> ( + int +): + return 2*a # Don't lose the comments -def double(a: int) -> int: # Hello - return 2 - * a - - -def double(a: int) -> int: - return 2 - * a +def double(a: int) -> ( # Hello + int +): + return 2*a +def double(a: int) -> ( + int # Hello +): + return 2*a # Really long annotations -def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: +def foo() -> ( + intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds +): return 2 - def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: return 2 - -def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds -| intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: +def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: return 2 - -def foo( - a: int, - b: int, - c: int, -) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: +def foo(a: int, b: int, c: int,) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: return 2 - -def foo( - a: int, - b: int, - c: int, -) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds -| intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: +def foo(a: int, b: int, c: int,) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: return 2 - # Split args but no need to split return -def foo( - a: int, - b: int, - c: int, -) -> int: +def foo(a: int, b: int, c: int,) -> int: return 2 - # Deeply nested brackets # with *interesting* spacing -def double(a: int) -> int: - return 2 - * a - - -def double(a: int) -> int: - return 2 - * a +def double(a: int) -> (((((int))))): + return 2*a +def double(a: int) -> ( + ( ( + ((int) + ) + ) + ) + ): + return 2*a -def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: +def foo() -> ( + ( ( + intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds +) +)): return 2 - # Return type with commas -def foo() -> tuple[int, int, int]: +def foo() -> ( + tuple[int, int, int] +): return 2 - -def foo() -> tuple[ - loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, - loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, - loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, -]: +def foo() -> tuple[loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong]: return 2 - # Magic trailing comma example -def foo() -> tuple[ - int, - int, - int, -]: +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 8bd41e4df11e1..4b710c3ddedeb 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,38 +60,24 @@ func( ```diff --- Black +++ Ruff -@@ -3,23 +3,61 @@ +@@ -3,23 +3,45 @@ b = tuple[int,] # But commas in multiple element subscripts should be removed. -c: tuple[int, int] -d = tuple[int, int] -+c: tuple[ -+ int, -+ int, -+] -+d = tuple[ -+ int, -+ int, -+] ++c: tuple[int, int,] ++d = tuple[int, int,] # Remove commas for non-subscripts. -small_list = [1] -list_of_types = [tuple[int,]] -small_set = {1} -set_of_types = {tuple[int,]} -+small_list = [ -+ 1, -+] -+list_of_types = [ -+ tuple[int,], -+] -+small_set = { -+ 1, -+} -+set_of_types = { -+ tuple[int,], -+} ++small_list = [1,] ++list_of_types = [tuple[int,],] ++small_set = {1,} ++set_of_types = {tuple[int,],} # Except single element tuples small_tuple = (1,) @@ -107,11 +93,7 @@ func( +) -func1(arg1).func2(arg2).func3(arg3).func4(arg4).func5(arg5) -+func1(arg1).func2( -+ arg2, -+).func3(arg3).func4( -+ arg4, -+).func5(arg5) ++func1(arg1).func2(arg2,).func3(arg3).func4(arg4,).func5(arg5) -(a, b, c, d) = func1(arg1) and func2(arg2) +( @@ -119,7 +101,9 @@ func( + b, + c, + d, -+) = func1(arg1) and func2(arg2) ++) = func1( ++ arg1 ++) and func2(arg2) -func(argument1, (one, two), argument4, argument5, argument6) +func( @@ -142,28 +126,14 @@ a: tuple[int,] b = tuple[int,] # But commas in multiple element subscripts should be removed. -c: tuple[ - int, - int, -] -d = tuple[ - int, - int, -] +c: tuple[int, int,] +d = tuple[int, int,] # Remove commas for non-subscripts. -small_list = [ - 1, -] -list_of_types = [ - tuple[int,], -] -small_set = { - 1, -} -set_of_types = { - tuple[int,], -} +small_list = [1,] +list_of_types = [tuple[int,],] +small_set = {1,} +set_of_types = {tuple[int,],} # Except single element tuples small_tuple = (1,) @@ -177,18 +147,16 @@ zero( five, ) -func1(arg1).func2( - arg2, -).func3(arg3).func4( - arg4, -).func5(arg5) +func1(arg1).func2(arg2,).func3(arg3).func4(arg4,).func5(arg5) ( a, b, c, d, -) = func1(arg1) and func2(arg2) +) = func1( + arg1 +) and func2(arg2) func( argument1, 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 new file mode 100644 index 0000000000000..902e4035e2ec4 --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__slices_py.snap @@ -0,0 +1,223 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/slices.py +--- +## Input + +```py +slice[a.b : c.d] +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: None : lambda: None] +slice[lambda x, y, *args, really=2, **kwargs: None :, 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)]] + + +async def f(): + slice[await x : [i async for i in arange(42)] : 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] +# ham[lower+offset : upper+offset] +ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)] +ham[lower + offset : upper + offset] + +slice[::, ::] +slice[ + # A + : + # B + : + # C +] +slice[ + # A + 1: + # B + 2: + # C + 3 +] + +slice[ + # A + 1 + + 2 : + # B + 3 : + # C + 4 +] +x[ + 1: # A + 2: # B + 3 # C +] +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -56,4 +56,8 @@ + # C + 4 + ] +-x[1:2:3] # A # B # C ++x[ ++ 1: # A ++ 2: # B ++ 3 # C ++] +``` + +## Ruff Output + +```py +slice[a.b : c.d] +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: None : lambda: None] +slice[lambda x, y, *args, really=2, **kwargs: None :, 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)]] + + +async def f(): + slice[await x : [i async for i in arange(42)] : 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] +# ham[lower+offset : upper+offset] +ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)] +ham[lower + offset : upper + offset] + +slice[::, ::] +slice[ + # A + : + # B + : + # C +] +slice[ + # A + 1: + # B + 2: + # C + 3 +] + +slice[ + # A + 1 + + 2 : + # B + 3 : + # C + 4 +] +x[ + 1: # A + 2: # B + 3 # C +] +``` + +## Black Output + +```py +slice[a.b : c.d] +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: None : lambda: None] +slice[lambda x, y, *args, really=2, **kwargs: None :, 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)]] + + +async def f(): + slice[await x : [i async for i in arange(42)] : 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] +# ham[lower+offset : upper+offset] +ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)] +ham[lower + offset : upper + offset] + +slice[::, ::] +slice[ + # A + : + # B + : + # C +] +slice[ + # A + 1: + # B + 2: + # C + 3 +] + +slice[ + # A + 1 + + 2 : + # B + 3 : + # C + 4 +] +x[1:2:3] # A # B # C +``` + + diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__string_prefixes_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__string_prefixes_py.snap index f2f517943447d..07e263b90e077 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__string_prefixes_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__string_prefixes_py.snap @@ -33,21 +33,25 @@ def docstring_multiline(): ```diff --- Black +++ Ruff -@@ -1,12 +1,12 @@ +@@ -1,13 +1,13 @@ #!/usr/bin/env python3 name = "Łukasz" -(f"hello {name}", f"hello {name}") +-(b"", b"") +-("", "") +(f"hello {name}", F"hello {name}") - (b"", b"") - ("", "") ++(b"", B"") ++(u"", U"") (r"", R"") -(rf"", rf"", Rf"", Rf"", rf"", rf"", Rf"", Rf"") +-(rb"", rb"", Rb"", Rb"", rb"", rb"", Rb"", Rb"") +(rf"", fr"", Rf"", fR"", rF"", Fr"", RF"", FR"") - (rb"", rb"", Rb"", Rb"", rb"", rb"", Rb"", Rb"") ++(rb"", br"", Rb"", bR"", rB"", Br"", RB"", BR"") + def docstring_singleline(): ``` ## Ruff Output @@ -57,12 +61,12 @@ def docstring_multiline(): name = "Łukasz" (f"hello {name}", F"hello {name}") -(b"", b"") -("", "") +(b"", B"") +(u"", U"") (r"", R"") (rf"", fr"", Rf"", fR"", rF"", Fr"", RF"", FR"") -(rb"", rb"", Rb"", Rb"", rb"", rb"", Rb"", Rb"") +(rb"", br"", Rb"", bR"", rB"", Br"", RB"", BR"") def docstring_singleline(): diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__torture_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__torture_py.snap index f59b077951ccc..8c44804b0de87 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__torture_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__torture_py.snap @@ -42,17 +42,17 @@ assert ( ```diff --- Black +++ Ruff -@@ -1,21 +1,23 @@ - importA - ( - () +@@ -1,58 +1,29 @@ +-importA +-( +- () - << 0 - ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525 -+ << 0**101234234242352525425252352352525234890264906820496920680926538059059209922523523525 - ) # +-) # ++importA;() << 0 ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525 # --assert sort_by_dependency( -- { + assert sort_by_dependency( + { - "1": {"2", "3"}, - "2": {"2a", "2b"}, - "3": {"3a", "3b"}, @@ -60,54 +60,50 @@ assert ( - "2b": set(), - "3a": set(), - "3b": set(), -- } --) == ["2a", "2b", "2", "3a", "3b", "3", "1"] -+assert ( -+ sort_by_dependency( -+ { -+ "1": {"2", "3"}, -+ "2": {"2a", "2b"}, -+ "3": {"3a", "3b"}, -+ "2a": set(), -+ "2b": set(), -+ "3a": set(), -+ "3b": set(), -+ } -+ ) -+ == ["2a", "2b", "2", "3a", "3b", "3", "1"] -+) ++ "1": {"2", "3"}, "2": {"2a", "2b"}, "3": {"3a", "3b"}, ++ "2a": set(), "2b": set(), "3a": set(), "3b": set() + } + ) == ["2a", "2b", "2", "3a", "3b", "3", "1"] importA - 0 -@@ -25,13 +27,12 @@ +-0 +-0 ^ 0 # +- ++0;0^0# + class A: def foo(self): for _ in range(10): - aaaaaaaaaaaaaaaaaaa = bbbbbbbbbbbbbbb.cccccccccc( -- xxxxxxxxxxxx ++ aaaaaaaaaaaaaaaaaaa = bbbbbbbbbbbbbbb.cccccccccc( # pylint: disable=no-member + xxxxxxxxxxxx - ) # pylint: disable=no-member -+ aaaaaaaaaaaaaaaaaaa = bbbbbbbbbbbbbbb.cccccccccc(xxxxxxxxxxxx) # pylint: disable=no-member - +- ++ ) def test(self, othr): - return 1 == 2 and ( -+ return 1 == 2 -+ and ( - name, - description, - self.default, -@@ -40,7 +41,8 @@ - self.parameters, - self.meta_data, - self.schedule, +- name, +- description, +- self.default, +- self.selected, +- self.auto_generated, +- self.parameters, +- self.meta_data, +- self.schedule, - ) == ( -+ ) -+ == ( - name, - description, - othr.default, -@@ -52,7 +54,13 @@ - ) +- name, +- description, +- othr.default, +- othr.selected, +- othr.auto_generated, +- othr.parameters, +- othr.meta_data, +- othr.schedule, +- ) ++ return (1 == 2 and ++ (name, description, self.default, self.selected, self.auto_generated, self.parameters, self.meta_data, self.schedule) == ++ (name, description, othr.default, othr.selected, othr.auto_generated, othr.parameters, othr.meta_data, othr.schedule)) -assert a_function( @@ -115,85 +111,42 @@ assert ( - which_is_eighty_eight_in_this_case_plus_a_bit_more, -) == {"x": "this need to pass the line limit as well", "b": "but only by a little bit"} +assert ( -+ a_function( -+ very_long_arguments_that_surpass_the_limit, -+ which_is_eighty_eight_in_this_case_plus_a_bit_more, -+ ) -+ == { -+ "x": "this need to pass the line limit as well", -+ "b": "but only by a little bit", -+ } ++ a_function(very_long_arguments_that_surpass_the_limit, which_is_eighty_eight_in_this_case_plus_a_bit_more) ++ == {"x": "this need to pass the line limit as well", "b": "but only by a little bit"} +) ``` ## Ruff Output ```py -importA -( - () - << 0**101234234242352525425252352352525234890264906820496920680926538059059209922523523525 -) # +importA;() << 0 ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525 # -assert ( - sort_by_dependency( - { - "1": {"2", "3"}, - "2": {"2a", "2b"}, - "3": {"3a", "3b"}, - "2a": set(), - "2b": set(), - "3a": set(), - "3b": set(), - } - ) - == ["2a", "2b", "2", "3a", "3b", "3", "1"] -) +assert sort_by_dependency( + { + "1": {"2", "3"}, "2": {"2a", "2b"}, "3": {"3a", "3b"}, + "2a": set(), "2b": set(), "3a": set(), "3b": set() + } +) == ["2a", "2b", "2", "3a", "3b", "3", "1"] importA -0 -0 ^ 0 # - +0;0^0# class A: def foo(self): for _ in range(10): - aaaaaaaaaaaaaaaaaaa = bbbbbbbbbbbbbbb.cccccccccc(xxxxxxxxxxxx) # pylint: disable=no-member - + aaaaaaaaaaaaaaaaaaa = bbbbbbbbbbbbbbb.cccccccccc( # pylint: disable=no-member + xxxxxxxxxxxx + ) def test(self, othr): - return 1 == 2 - and ( - name, - description, - self.default, - self.selected, - self.auto_generated, - self.parameters, - self.meta_data, - self.schedule, - ) - == ( - name, - description, - othr.default, - othr.selected, - othr.auto_generated, - othr.parameters, - othr.meta_data, - othr.schedule, - ) + return (1 == 2 and + (name, description, self.default, self.selected, self.auto_generated, self.parameters, self.meta_data, self.schedule) == + (name, description, othr.default, othr.selected, othr.auto_generated, othr.parameters, othr.meta_data, othr.schedule)) assert ( - a_function( - very_long_arguments_that_surpass_the_limit, - which_is_eighty_eight_in_this_case_plus_a_bit_more, - ) - == { - "x": "this need to pass the line limit as well", - "b": "but only by a little bit", - } + a_function(very_long_arguments_that_surpass_the_limit, which_is_eighty_eight_in_this_case_plus_a_bit_more) + == {"x": "this need to pass the line limit as well", "b": "but only by a little bit"} ) ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens1_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens1_py.snap index 3511af0fc9490..c3362ebff7e09 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens1_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens1_py.snap @@ -38,64 +38,53 @@ class A: ```diff --- Black +++ Ruff -@@ -1,7 +1,6 @@ +@@ -1,19 +1,11 @@ -if e1234123412341234.winerror not in ( - _winapi.ERROR_SEM_TIMEOUT, - _winapi.ERROR_PIPE_BUSY, -) or _check_timeout(t): -+if e1234123412341234.winerror -+not in (_winapi.ERROR_SEM_TIMEOUT, _winapi.ERROR_PIPE_BUSY) -+or _check_timeout(t): ++if e1234123412341234.winerror not in (_winapi.ERROR_SEM_TIMEOUT, ++ _winapi.ERROR_PIPE_BUSY) or _check_timeout(t): pass if x: -@@ -21,14 +20,20 @@ + if y: +- new_id = ( +- max( +- Vegetable.objects.order_by("-id")[0].id, +- Mineral.objects.order_by("-id")[0].id, +- ) +- + 1 +- ) +- ++ new_id = max(Vegetable.objects.order_by('-id')[0].id, ++ Mineral.objects.order_by('-id')[0].id) + 1 + + class X: + def get_help_text(self): +@@ -21,8 +13,7 @@ "Your password must contain at least %(min_length)d character.", "Your password must contain at least %(min_length)d characters.", self.min_length, - ) % {"min_length": self.min_length} -+ ) -+ % {"min_length": self.min_length} - +- ++ ) % {'min_length': self.min_length} class A: def b(self): -- if self.connection.mysql_is_mariadb and ( -- 10, -- 4, -- 3, -- ) < self.connection.mysql_version < (10, 5, 2): -+ if ( -+ self.connection.mysql_is_mariadb -+ and ( -+ 10, -+ 4, -+ 3, -+ ) -+ < self.connection.mysql_version -+ < (10, 5, 2) -+ ): - pass ``` ## Ruff Output ```py -if e1234123412341234.winerror -not in (_winapi.ERROR_SEM_TIMEOUT, _winapi.ERROR_PIPE_BUSY) -or _check_timeout(t): +if e1234123412341234.winerror not in (_winapi.ERROR_SEM_TIMEOUT, + _winapi.ERROR_PIPE_BUSY) or _check_timeout(t): pass if x: if y: - new_id = ( - max( - Vegetable.objects.order_by("-id")[0].id, - Mineral.objects.order_by("-id")[0].id, - ) - + 1 - ) - + new_id = max(Vegetable.objects.order_by('-id')[0].id, + Mineral.objects.order_by('-id')[0].id) + 1 class X: def get_help_text(self): @@ -103,22 +92,15 @@ class X: "Your password must contain at least %(min_length)d character.", "Your password must contain at least %(min_length)d characters.", self.min_length, - ) - % {"min_length": self.min_length} - + ) % {'min_length': self.min_length} class A: def b(self): - if ( - self.connection.mysql_is_mariadb - and ( - 10, - 4, - 3, - ) - < self.connection.mysql_version - < (10, 5, 2) - ): + if self.connection.mysql_is_mariadb and ( + 10, + 4, + 3, + ) < self.connection.mysql_version < (10, 5, 2): pass ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens2_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens2_py.snap index edc16d0dce1ff..4b8ed29e06fcf 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens2_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens2_py.snap @@ -16,26 +16,22 @@ if (e123456.get_tk_patchlevel() >= (8, 6, 0, 'final') or ```diff --- Black +++ Ruff -@@ -1,6 +1,5 @@ +@@ -1,6 +1,3 @@ -if e123456.get_tk_patchlevel() >= (8, 6, 0, "final") or ( - 8, - 5, - 8, -) <= get_tk_patchlevel() < (8, 6): -+if ( -+ e123456.get_tk_patchlevel() >= (8, 6, 0, "final") -+ or (8, 5, 8) <= get_tk_patchlevel() < (8, 6) -+): ++if (e123456.get_tk_patchlevel() >= (8, 6, 0, 'final') or ++ (8, 5, 8) <= get_tk_patchlevel() < (8, 6)): pass ``` ## Ruff Output ```py -if ( - e123456.get_tk_patchlevel() >= (8, 6, 0, "final") - or (8, 5, 8) <= get_tk_patchlevel() < (8, 6) -): +if (e123456.get_tk_patchlevel() >= (8, 6, 0, 'final') or + (8, 5, 8) <= get_tk_patchlevel() < (8, 6)): pass ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens3_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens3_py.snap deleted file mode 100644 index c7af64f403b9c..0000000000000 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__trailing_comma_optional_parens3_py.snap +++ /dev/null @@ -1,60 +0,0 @@ ---- -source: crates/ruff_python_formatter/src/lib.rs -expression: snapshot -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/trailing_comma_optional_parens3.py ---- -## Input - -```py -if True: - if True: - if True: - return _( - "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweas " - + "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwegqweasdzxcqweasdzxc.", - "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwe", - ) % {"reported_username": reported_username, "report_reason": report_reason} -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -5,4 +5,5 @@ - "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweas " - + "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwegqweasdzxcqweasdzxc.", - "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwe", -- ) % {"reported_username": reported_username, "report_reason": report_reason} -+ ) -+ % {"reported_username": reported_username, "report_reason": report_reason} -``` - -## Ruff Output - -```py -if True: - if True: - if True: - return _( - "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweas " - + "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwegqweasdzxcqweasdzxc.", - "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwe", - ) - % {"reported_username": reported_username, "report_reason": report_reason} -``` - -## Black Output - -```py -if True: - if True: - if True: - return _( - "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweas " - + "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwegqweasdzxcqweasdzxc.", - "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwe", - ) % {"reported_username": reported_username, "report_reason": report_reason} -``` - - 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 c32d073085182..80f18068cb1d1 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,55 +46,51 @@ assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx( ```diff --- Black +++ Ruff -@@ -20,9 +20,7 @@ - b, - c, - d, +@@ -1,28 +1,11 @@ +-zero( +- one, +-).two( +- three, +-).four( +- five, +-) ++zero(one,).two(three,).four(five,) + +-func1(arg1).func2( +- arg2, +-).func3(arg3).func4( +- arg4, +-).func5(arg5) ++func1(arg1).func2(arg2,).func3(arg3).func4(arg4,).func5(arg5) + + # Inner one-element tuple shouldn't explode + func1(arg1).func2(arg1, (one_tuple,)).func3(arg3) + +-( +- a, +- b, +- c, +- d, -) = func1( - arg1 -) and func2(arg2) -+) = func1(arg1) and func2(arg2) ++(a, b, c, d,) = func1(arg1) and func2(arg2) # Example from https://github.com/psf/black/issues/3229 -@@ -43,8 +41,6 @@ - ) - - # Regression test for https://github.com/psf/black/issues/3414. --assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx( -- xxxxxxxxx --).xxxxxxxxxxxxxxxxxx(), ( -+assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx(xxxxxxxxx).xxxxxxxxxxxxxxxxxx(), ( - "xxx {xxxxxxxxx} xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - ) ``` ## Ruff Output ```py -zero( - one, -).two( - three, -).four( - five, -) +zero(one,).two(three,).four(five,) -func1(arg1).func2( - arg2, -).func3(arg3).func4( - arg4, -).func5(arg5) +func1(arg1).func2(arg2,).func3(arg3).func4(arg4,).func5(arg5) # Inner one-element tuple shouldn't explode func1(arg1).func2(arg1, (one_tuple,)).func3(arg3) -( - a, - b, - c, - d, -) = func1(arg1) and func2(arg2) +(a, b, c, d,) = func1(arg1) and func2(arg2) # Example from https://github.com/psf/black/issues/3229 @@ -115,7 +111,9 @@ assert ( ) # Regression test for https://github.com/psf/black/issues/3414. -assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx(xxxxxxxxx).xxxxxxxxxxxxxxxxxx(), ( +assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx( + xxxxxxxxx +).xxxxxxxxxxxxxxxxxx(), ( "xxx {xxxxxxxxx} xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ) ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__tupleassign_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__tupleassign_py.snap index d5bdb5382dc11..d4c4d3546f9a3 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__tupleassign_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__tupleassign_py.snap @@ -20,30 +20,31 @@ this_will_be_wrapped_in_parens, = struct.unpack(b"12345678901234567890") ```diff --- Black +++ Ruff -@@ -4,7 +4,7 @@ - sdfjsdfjlksdljkfsdlkf, - sdfsdjfklsdfjlksdljkf, - sdsfsdfjskdflsfsdf, +@@ -1,12 +1,7 @@ + # This is a standalone comment. +-( +- sdfjklsdfsjldkflkjsf, +- sdfjsdfjlksdljkfsdlkf, +- sdfsdjfklsdfjlksdljkf, +- sdsfsdfjskdflsfsdf, -) = (1, 2, 3) -+) = 1, 2, 3 ++sdfjklsdfsjldkflkjsf, sdfjsdfjlksdljkfsdlkf, sdfsdjfklsdfjlksdljkf, sdsfsdfjskdflsfsdf = 1, 2, 3 # This is as well. - (this_will_be_wrapped_in_parens,) = struct.unpack(b"12345678901234567890") +-(this_will_be_wrapped_in_parens,) = struct.unpack(b"12345678901234567890") ++this_will_be_wrapped_in_parens, = struct.unpack(b"12345678901234567890") + + (a,) = call() ``` ## Ruff Output ```py # This is a standalone comment. -( - sdfjklsdfsjldkflkjsf, - sdfjsdfjlksdljkfsdlkf, - sdfsdjfklsdfjlksdljkf, - sdsfsdfjskdflsfsdf, -) = 1, 2, 3 +sdfjklsdfsjldkflkjsf, sdfjsdfjlksdljkfsdlkf, sdfsdjfklsdfjlksdljkf, sdsfsdfjskdflsfsdf = 1, 2, 3 # This is as well. -(this_will_be_wrapped_in_parens,) = struct.unpack(b"12345678901234567890") +this_will_be_wrapped_in_parens, = struct.unpack(b"12345678901234567890") (a,) = call() ``` diff --git a/crates/ruff_python_formatter/src/trivia.rs b/crates/ruff_python_formatter/src/trivia.rs deleted file mode 100644 index cab42fd7b6d70..0000000000000 --- a/crates/ruff_python_formatter/src/trivia.rs +++ /dev/null @@ -1,1031 +0,0 @@ -use ruff_text_size::{TextRange, TextSize}; -use rustc_hash::FxHashMap; -use rustpython_parser::lexer::LexResult; -use rustpython_parser::Tok; - -use crate::cst::{ - Alias, Arg, Body, BoolOp, CmpOp, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Keyword, - Operator, Pattern, PatternKind, SliceIndex, SliceIndexKind, Stmt, StmtKind, UnaryOp, -}; - -#[derive(Clone, Copy, Debug)] -pub(crate) enum Node<'a> { - Alias(&'a Alias), - Arg(&'a Arg), - Body(&'a Body), - BoolOp(&'a BoolOp), - CmpOp(&'a CmpOp), - Excepthandler(&'a Excepthandler), - Expr(&'a Expr), - Keyword(&'a Keyword), - Mod(&'a [Stmt]), - Operator(&'a Operator), - Pattern(&'a Pattern), - SliceIndex(&'a SliceIndex), - Stmt(&'a Stmt), - UnaryOp(&'a UnaryOp), -} - -impl Node<'_> { - pub(crate) fn id(&self) -> usize { - match self { - Node::Alias(node) => node.id(), - Node::Arg(node) => node.id(), - Node::Body(node) => node.id(), - Node::BoolOp(node) => node.id(), - Node::CmpOp(node) => node.id(), - Node::Excepthandler(node) => node.id(), - Node::Expr(node) => node.id(), - Node::Keyword(node) => node.id(), - Node::Mod(nodes) => nodes as *const _ as usize, - Node::Operator(node) => node.id(), - Node::Pattern(node) => node.id(), - Node::SliceIndex(node) => node.id(), - Node::Stmt(node) => node.id(), - Node::UnaryOp(node) => node.id(), - } - } - - pub(crate) fn start(&self) -> TextSize { - match self { - Node::Alias(node) => node.start(), - Node::Arg(node) => node.start(), - Node::Body(node) => node.start(), - Node::BoolOp(node) => node.start(), - Node::CmpOp(node) => node.start(), - Node::Excepthandler(node) => node.start(), - Node::Expr(node) => node.start(), - Node::Keyword(node) => node.start(), - Node::Mod(..) => unreachable!("Node::Mod cannot be a child node"), - Node::Operator(node) => node.start(), - Node::Pattern(node) => node.start(), - Node::SliceIndex(node) => node.start(), - Node::Stmt(node) => node.start(), - Node::UnaryOp(node) => node.start(), - } - } - - pub(crate) fn end(&self) -> TextSize { - match self { - Node::Alias(node) => node.end(), - Node::Arg(node) => node.end(), - Node::Body(node) => node.end(), - Node::BoolOp(node) => node.end(), - Node::CmpOp(node) => node.end(), - Node::Excepthandler(node) => node.end(), - Node::Expr(node) => node.end(), - Node::Keyword(node) => node.end(), - Node::Mod(..) => unreachable!("Node::Mod cannot be a child node"), - Node::Operator(node) => node.end(), - Node::Pattern(node) => node.end(), - Node::SliceIndex(node) => node.end(), - Node::Stmt(node) => node.end(), - Node::UnaryOp(node) => node.end(), - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub(crate) enum TriviaTokenKind { - OwnLineComment, - EndOfLineComment, - MagicTrailingComma, - EmptyLine, - Parentheses, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) struct TriviaToken { - pub(crate) range: TextRange, - pub(crate) kind: TriviaTokenKind, -} - -impl TriviaToken { - pub(crate) const fn start(&self) -> TextSize { - self.range.start() - } - - pub(crate) const fn end(&self) -> TextSize { - self.range.end() - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, is_macro::Is)] -pub(crate) enum TriviaKind { - /// A Comment that is separated by at least one line break from the - /// preceding token. - /// - /// # Examples - /// - /// ```ignore - /// a = 1 - /// # This is an own-line comment. - /// b = 2 - /// ``` - OwnLineComment(TextRange), - /// A comment that is on the same line as the preceding token. - /// - /// # Examples - /// - /// ## End of line - /// - /// ```ignore - /// a = 1 # This is an end-of-line comment. - /// b = 2 - /// ``` - EndOfLineComment(TextRange), - MagicTrailingComma, - EmptyLine, - Parentheses, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, is_macro::Is)] -pub(crate) enum Relationship { - Leading, - Trailing, - Dangling, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, is_macro::Is)] -pub(crate) enum Parenthesize { - /// Always parenthesize the statement or expression. - Always, - /// Never parenthesize the statement or expression. - Never, - /// Parenthesize the statement or expression if it expands. - IfExpanded, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) struct Trivia { - pub(crate) kind: TriviaKind, - pub(crate) relationship: Relationship, -} - -impl Trivia { - pub(crate) fn from_token(token: &TriviaToken, relationship: Relationship) -> Self { - match token.kind { - TriviaTokenKind::MagicTrailingComma => Self { - kind: TriviaKind::MagicTrailingComma, - relationship, - }, - TriviaTokenKind::EmptyLine => Self { - kind: TriviaKind::EmptyLine, - relationship, - }, - TriviaTokenKind::OwnLineComment => Self { - kind: TriviaKind::OwnLineComment(token.range), - relationship, - }, - TriviaTokenKind::EndOfLineComment => Self { - kind: TriviaKind::EndOfLineComment(token.range), - relationship, - }, - TriviaTokenKind::Parentheses => Self { - kind: TriviaKind::Parentheses, - relationship, - }, - } - } -} - -pub(crate) fn extract_trivia_tokens(lxr: &[LexResult]) -> Vec { - let mut tokens = vec![]; - let mut prev_tok: Option<(&Tok, TextRange)> = None; - let mut prev_semantic_tok: Option<(&Tok, TextRange)> = None; - let mut parens = vec![]; - - for (tok, range) in lxr.iter().flatten() { - let after_new_line = matches!(prev_tok, Some((Tok::Newline | Tok::NonLogicalNewline, _))); - - // Add empty lines. - if after_new_line && matches!(tok, Tok::NonLogicalNewline) { - tokens.push(TriviaToken { - range: *range, - kind: TriviaTokenKind::EmptyLine, - }); - } - - // Add comments. - if matches!(tok, Tok::Comment(..)) { - tokens.push(TriviaToken { - range: *range, - // Used to use prev_non-newline_tok - kind: if after_new_line || prev_tok.is_none() { - TriviaTokenKind::OwnLineComment - } else { - TriviaTokenKind::EndOfLineComment - }, - }); - } - - // Add magic trailing commas. - if matches!( - tok, - Tok::Rpar | Tok::Rsqb | Tok::Rbrace | Tok::Equal | Tok::Newline - ) { - if let Some((prev_tok, prev_range)) = prev_semantic_tok { - if prev_tok == &Tok::Comma { - tokens.push(TriviaToken { - range: prev_range, - kind: TriviaTokenKind::MagicTrailingComma, - }); - } - } - } - - if matches!(tok, Tok::Lpar) { - if prev_tok.map_or(true, |(prev_tok, _)| { - !matches!( - prev_tok, - Tok::Name { .. } - | Tok::Int { .. } - | Tok::Float { .. } - | Tok::Complex { .. } - | Tok::String { .. } - ) - }) { - parens.push((range.start(), true)); - } else { - parens.push((range.start(), false)); - } - } else if matches!(tok, Tok::Rpar) { - let (start, explicit) = parens.pop().unwrap(); - if explicit { - tokens.push(TriviaToken { - range: TextRange::new(start, range.end()), - kind: TriviaTokenKind::Parentheses, - }); - } - } - - prev_tok = Some((tok, *range)); - - // Track the most recent semantic token. - if !matches!( - tok, - Tok::Newline | Tok::NonLogicalNewline | Tok::Comment(..) - ) { - prev_semantic_tok = Some((tok, *range)); - } - } - tokens -} - -fn sorted_child_nodes_inner<'a>(node: Node<'a>, result: &mut Vec>) { - match node { - Node::Mod(nodes) => { - for stmt in nodes.iter() { - result.push(Node::Stmt(stmt)); - } - } - Node::Body(body) => { - result.extend(body.iter().map(Node::Stmt)); - } - Node::Stmt(stmt) => match &stmt.node { - StmtKind::Return { value } => { - if let Some(value) = value { - result.push(Node::Expr(value)); - } - } - StmtKind::Expr { value } => { - result.push(Node::Expr(value)); - } - StmtKind::Pass => {} - StmtKind::Assign { targets, value, .. } => { - for target in targets { - result.push(Node::Expr(target)); - } - result.push(Node::Expr(value)); - } - StmtKind::FunctionDef { - args, - body, - decorator_list, - returns, - .. - } - | StmtKind::AsyncFunctionDef { - args, - body, - decorator_list, - returns, - .. - } => { - for decorator in decorator_list { - result.push(Node::Expr(decorator)); - } - for arg in &args.posonlyargs { - result.push(Node::Arg(arg)); - } - for arg in &args.args { - result.push(Node::Arg(arg)); - } - if let Some(arg) = &args.vararg { - result.push(Node::Arg(arg)); - } - for arg in &args.kwonlyargs { - result.push(Node::Arg(arg)); - } - if let Some(arg) = &args.kwarg { - result.push(Node::Arg(arg)); - } - for expr in &args.defaults { - result.push(Node::Expr(expr)); - } - for expr in &args.kw_defaults { - result.push(Node::Expr(expr)); - } - if let Some(returns) = returns { - result.push(Node::Expr(returns)); - } - result.push(Node::Body(body)); - } - StmtKind::ClassDef { - bases, - keywords, - body, - decorator_list, - .. - } => { - for decorator in decorator_list { - result.push(Node::Expr(decorator)); - } - for base in bases { - result.push(Node::Expr(base)); - } - for keyword in keywords { - result.push(Node::Keyword(keyword)); - } - result.push(Node::Body(body)); - } - StmtKind::Delete { targets } => { - for target in targets { - result.push(Node::Expr(target)); - } - } - StmtKind::AugAssign { target, op, value } => { - result.push(Node::Expr(target)); - result.push(Node::Operator(op)); - result.push(Node::Expr(value)); - } - StmtKind::AnnAssign { - target, - annotation, - value, - .. - } => { - result.push(Node::Expr(target)); - result.push(Node::Expr(annotation)); - if let Some(value) = value { - result.push(Node::Expr(value)); - } - } - StmtKind::For { - target, - iter, - body, - orelse, - .. - } - | StmtKind::AsyncFor { - target, - iter, - body, - orelse, - .. - } => { - result.push(Node::Expr(target)); - result.push(Node::Expr(iter)); - result.push(Node::Body(body)); - if let Some(orelse) = orelse { - result.push(Node::Body(orelse)); - } - } - StmtKind::While { test, body, orelse } => { - result.push(Node::Expr(test)); - result.push(Node::Body(body)); - if let Some(orelse) = orelse { - result.push(Node::Body(orelse)); - } - } - StmtKind::If { - test, body, orelse, .. - } => { - result.push(Node::Expr(test)); - result.push(Node::Body(body)); - if let Some(orelse) = orelse { - result.push(Node::Body(orelse)); - } - } - StmtKind::With { items, body, .. } | StmtKind::AsyncWith { items, body, .. } => { - for item in items { - result.push(Node::Expr(&item.context_expr)); - if let Some(expr) = &item.optional_vars { - result.push(Node::Expr(expr)); - } - } - result.push(Node::Body(body)); - } - StmtKind::Match { subject, cases } => { - result.push(Node::Expr(subject)); - for case in cases { - result.push(Node::Pattern(&case.pattern)); - if let Some(expr) = &case.guard { - result.push(Node::Expr(expr)); - } - result.push(Node::Body(&case.body)); - } - } - StmtKind::Raise { exc, cause } => { - if let Some(exc) = exc { - result.push(Node::Expr(exc)); - } - if let Some(cause) = cause { - result.push(Node::Expr(cause)); - } - } - StmtKind::Assert { test, msg } => { - result.push(Node::Expr(test)); - if let Some(msg) = msg { - result.push(Node::Expr(msg)); - } - } - StmtKind::Break => {} - StmtKind::Continue => {} - StmtKind::Try { - body, - handlers, - orelse, - finalbody, - } - | StmtKind::TryStar { - body, - handlers, - orelse, - finalbody, - } => { - result.push(Node::Body(body)); - for handler in handlers { - result.push(Node::Excepthandler(handler)); - } - if let Some(orelse) = orelse { - result.push(Node::Body(orelse)); - } - if let Some(finalbody) = finalbody { - result.push(Node::Body(finalbody)); - } - } - StmtKind::Import { names } => { - for name in names { - result.push(Node::Alias(name)); - } - } - StmtKind::ImportFrom { names, .. } => { - for name in names { - result.push(Node::Alias(name)); - } - } - StmtKind::Global { .. } => {} - StmtKind::Nonlocal { .. } => {} - }, - Node::Arg(arg) => { - if let Some(annotation) = &arg.node.annotation { - result.push(Node::Expr(annotation)); - } - } - Node::Expr(expr) => match &expr.node { - ExprKind::BoolOp { ops, values } => { - result.push(Node::Expr(&values[0])); - for (op, value) in ops.iter().zip(&values[1..]) { - result.push(Node::BoolOp(op)); - result.push(Node::Expr(value)); - } - } - ExprKind::NamedExpr { target, value } => { - result.push(Node::Expr(target)); - result.push(Node::Expr(value)); - } - ExprKind::BinOp { left, op, right } => { - result.push(Node::Expr(left)); - result.push(Node::Operator(op)); - result.push(Node::Expr(right)); - } - ExprKind::UnaryOp { op, operand } => { - result.push(Node::UnaryOp(op)); - result.push(Node::Expr(operand)); - } - ExprKind::Lambda { body, args, .. } => { - for expr in &args.defaults { - result.push(Node::Expr(expr)); - } - for expr in &args.kw_defaults { - result.push(Node::Expr(expr)); - } - result.push(Node::Expr(body)); - } - ExprKind::IfExp { test, body, orelse } => { - result.push(Node::Expr(body)); - result.push(Node::Expr(test)); - result.push(Node::Expr(orelse)); - } - ExprKind::Dict { keys, values } => { - for key in keys.iter().flatten() { - result.push(Node::Expr(key)); - } - for value in values { - result.push(Node::Expr(value)); - } - } - ExprKind::Set { elts } => { - for elt in elts { - result.push(Node::Expr(elt)); - } - } - ExprKind::ListComp { elt, generators } => { - result.push(Node::Expr(elt)); - for generator in generators { - result.push(Node::Expr(&generator.target)); - result.push(Node::Expr(&generator.iter)); - for expr in &generator.ifs { - result.push(Node::Expr(expr)); - } - } - } - ExprKind::SetComp { elt, generators } => { - result.push(Node::Expr(elt)); - for generator in generators { - result.push(Node::Expr(&generator.target)); - result.push(Node::Expr(&generator.iter)); - for expr in &generator.ifs { - result.push(Node::Expr(expr)); - } - } - } - ExprKind::DictComp { - key, - value, - generators, - } => { - result.push(Node::Expr(key)); - result.push(Node::Expr(value)); - for generator in generators { - result.push(Node::Expr(&generator.target)); - result.push(Node::Expr(&generator.iter)); - for expr in &generator.ifs { - result.push(Node::Expr(expr)); - } - } - } - ExprKind::GeneratorExp { elt, generators } => { - result.push(Node::Expr(elt)); - for generator in generators { - result.push(Node::Expr(&generator.target)); - result.push(Node::Expr(&generator.iter)); - for expr in &generator.ifs { - result.push(Node::Expr(expr)); - } - } - } - ExprKind::Await { value } => { - result.push(Node::Expr(value)); - } - ExprKind::Yield { value } => { - if let Some(value) = value { - result.push(Node::Expr(value)); - } - } - ExprKind::YieldFrom { value } => { - result.push(Node::Expr(value)); - } - ExprKind::Compare { - left, - ops, - comparators, - } => { - result.push(Node::Expr(left)); - for (op, comparator) in ops.iter().zip(comparators) { - result.push(Node::CmpOp(op)); - result.push(Node::Expr(comparator)); - } - } - ExprKind::Call { - func, - args, - keywords, - } => { - result.push(Node::Expr(func)); - for arg in args { - result.push(Node::Expr(arg)); - } - for keyword in keywords { - result.push(Node::Keyword(keyword)); - } - } - ExprKind::FormattedValue { - value, format_spec, .. - } => { - result.push(Node::Expr(value)); - if let Some(format_spec) = format_spec { - result.push(Node::Expr(format_spec)); - } - } - ExprKind::JoinedStr { values } => { - for value in values { - result.push(Node::Expr(value)); - } - } - ExprKind::Constant { .. } => {} - ExprKind::Attribute { value, .. } => { - result.push(Node::Expr(value)); - } - ExprKind::Subscript { value, slice, .. } => { - result.push(Node::Expr(value)); - result.push(Node::Expr(slice)); - } - ExprKind::Starred { value, .. } => { - result.push(Node::Expr(value)); - } - - ExprKind::Name { .. } => {} - ExprKind::List { elts, .. } => { - for elt in elts { - result.push(Node::Expr(elt)); - } - } - ExprKind::Tuple { elts, .. } => { - for elt in elts { - result.push(Node::Expr(elt)); - } - } - ExprKind::Slice { lower, upper, step } => { - result.push(Node::SliceIndex(lower)); - result.push(Node::SliceIndex(upper)); - if let Some(step) = step { - result.push(Node::SliceIndex(step)); - } - } - }, - Node::Keyword(keyword) => { - result.push(Node::Expr(&keyword.node.value)); - } - Node::Alias(..) => {} - Node::Excepthandler(excepthandler) => { - let ExcepthandlerKind::ExceptHandler { type_, body, .. } = &excepthandler.node; - if let Some(type_) = type_ { - result.push(Node::Expr(type_)); - } - result.push(Node::Body(body)); - } - Node::SliceIndex(slice_index) => { - if let SliceIndexKind::Index { value } = &slice_index.node { - result.push(Node::Expr(value)); - } - } - Node::Pattern(pattern) => match &pattern.node { - PatternKind::MatchValue { value } => { - result.push(Node::Expr(value)); - } - PatternKind::MatchSingleton { .. } => {} - PatternKind::MatchSequence { patterns } => { - for pattern in patterns { - result.push(Node::Pattern(pattern)); - } - } - PatternKind::MatchMapping { keys, patterns, .. } => { - for (key, pattern) in keys.iter().zip(patterns.iter()) { - result.push(Node::Expr(key)); - result.push(Node::Pattern(pattern)); - } - } - PatternKind::MatchClass { - cls, - patterns, - kwd_patterns, - .. - } => { - result.push(Node::Expr(cls)); - for pattern in patterns { - result.push(Node::Pattern(pattern)); - } - for pattern in kwd_patterns { - result.push(Node::Pattern(pattern)); - } - } - PatternKind::MatchStar { .. } => {} - PatternKind::MatchAs { pattern, .. } => { - if let Some(pattern) = pattern { - result.push(Node::Pattern(pattern)); - } - } - PatternKind::MatchOr { patterns } => { - for pattern in patterns { - result.push(Node::Pattern(pattern)); - } - } - }, - Node::BoolOp(..) => {} - Node::UnaryOp(..) => {} - Node::Operator(..) => {} - Node::CmpOp(..) => {} - } -} - -pub(crate) fn sorted_child_nodes(node: Node) -> Vec { - let mut result = Vec::new(); - sorted_child_nodes_inner(node, &mut result); - - result -} - -pub(crate) fn decorate_token<'a>( - token: &TriviaToken, - node: Node<'a>, - enclosing_node: Option>, - enclosed_node: Option>, - cache: &mut FxHashMap>>, -) -> ( - Option>, - Option>, - Option>, - Option>, -) { - let child_nodes = cache - .entry(node.id()) - .or_insert_with(|| sorted_child_nodes(node)); - - let mut preceding_node = None; - let mut following_node = None; - let mut enclosed_node = enclosed_node; - - let mut left = 0; - let mut right = child_nodes.len(); - - while left < right { - let middle = (left + right) / 2; - let child = child_nodes[middle]; - let start = child.start(); - let end = child.end(); - - if let Some(existing) = &enclosed_node { - // Special-case: if we're dealing with a statement that's a single expression, - // we want to treat the expression as the enclosed node. - let existing_start = existing.start(); - let existing_end = existing.end(); - if start == existing_start && end == existing_end { - enclosed_node = Some(child); - } - } else { - if token.start() <= start && token.end() >= end { - enclosed_node = Some(child); - } - } - - // The comment is completely contained by this child node. - if token.start() >= start && token.end() <= end { - return decorate_token(token, child, Some(child), enclosed_node, cache); - } - - if end <= token.start() { - // This child node falls completely before the comment. - // Because we will never consider this node or any nodes - // before it again, this node must be the closest preceding - // node we have encountered so far. - preceding_node = Some(child); - left = middle + 1; - continue; - } - - if token.end() <= start { - // This child node falls completely after the comment. - // Because we will never consider this node or any nodes after - // it again, this node must be the closest following node we - // have encountered so far. - following_node = Some(child); - right = middle; - continue; - } - - return (None, None, None, enclosed_node); - } - - ( - preceding_node, - following_node, - enclosing_node, - enclosed_node, - ) -} - -#[derive(Debug, Default)] -pub(crate) struct TriviaIndex { - pub(crate) alias: FxHashMap>, - pub(crate) arg: FxHashMap>, - pub(crate) body: FxHashMap>, - pub(crate) bool_op: FxHashMap>, - pub(crate) cmp_op: FxHashMap>, - pub(crate) excepthandler: FxHashMap>, - pub(crate) expr: FxHashMap>, - pub(crate) keyword: FxHashMap>, - pub(crate) operator: FxHashMap>, - pub(crate) pattern: FxHashMap>, - pub(crate) slice_index: FxHashMap>, - pub(crate) stmt: FxHashMap>, - pub(crate) unary_op: FxHashMap>, -} - -fn add_comment(comment: Trivia, node: &Node, trivia: &mut TriviaIndex) { - match node { - Node::Alias(node) => { - trivia - .alias - .entry(node.id()) - .or_insert_with(Vec::new) - .push(comment); - } - Node::Arg(node) => { - trivia - .arg - .entry(node.id()) - .or_insert_with(Vec::new) - .push(comment); - } - Node::Body(node) => { - trivia - .body - .entry(node.id()) - .or_insert_with(Vec::new) - .push(comment); - } - Node::BoolOp(node) => { - trivia - .bool_op - .entry(node.id()) - .or_insert_with(Vec::new) - .push(comment); - } - Node::CmpOp(node) => { - trivia - .cmp_op - .entry(node.id()) - .or_insert_with(Vec::new) - .push(comment); - } - Node::Excepthandler(node) => { - trivia - .excepthandler - .entry(node.id()) - .or_insert_with(Vec::new) - .push(comment); - } - Node::Expr(node) => { - trivia - .expr - .entry(node.id()) - .or_insert_with(Vec::new) - .push(comment); - } - Node::Keyword(node) => { - trivia - .keyword - .entry(node.id()) - .or_insert_with(Vec::new) - .push(comment); - } - Node::Operator(node) => { - trivia - .operator - .entry(node.id()) - .or_insert_with(Vec::new) - .push(comment); - } - Node::Pattern(node) => { - trivia - .pattern - .entry(node.id()) - .or_insert_with(Vec::new) - .push(comment); - } - Node::SliceIndex(node) => { - trivia - .slice_index - .entry(node.id()) - .or_insert_with(Vec::new) - .push(comment); - } - Node::Stmt(node) => { - trivia - .stmt - .entry(node.id()) - .or_insert_with(Vec::new) - .push(comment); - } - Node::UnaryOp(node) => { - trivia - .unary_op - .entry(node.id()) - .or_insert_with(Vec::new) - .push(comment); - } - Node::Mod(_) => {} - } -} - -pub(crate) fn decorate_trivia(tokens: Vec, python_ast: &[Stmt]) -> TriviaIndex { - let mut stack = vec![]; - let mut cache = FxHashMap::default(); - for token in &tokens { - let (preceding_node, following_node, enclosing_node, enclosed_node) = - decorate_token(token, Node::Mod(python_ast), None, None, &mut cache); - - stack.push(( - preceding_node, - following_node, - enclosing_node, - enclosed_node, - )); - } - - let mut trivia_index = TriviaIndex::default(); - - for (index, token) in tokens.into_iter().enumerate() { - let (preceding_node, following_node, enclosing_node, enclosed_node) = &stack[index]; - match token.kind { - TriviaTokenKind::EmptyLine | TriviaTokenKind::OwnLineComment => { - if let Some(following_node) = following_node { - // Always a leading comment. - add_comment( - Trivia::from_token(&token, Relationship::Leading), - following_node, - &mut trivia_index, - ); - } else if let Some(enclosing_node) = enclosing_node { - // TODO(charlie): Prettier puts this `else if` after `preceding_note`. - add_comment( - Trivia::from_token(&token, Relationship::Dangling), - enclosing_node, - &mut trivia_index, - ); - } else if let Some(preceding_node) = preceding_node { - add_comment( - Trivia::from_token(&token, Relationship::Trailing), - preceding_node, - &mut trivia_index, - ); - } else { - unreachable!("Attach token to the ast: {:?}", token); - } - } - TriviaTokenKind::EndOfLineComment => { - if let Some(preceding_node) = preceding_node { - // There is content before this comment on the same line, but - // none after it, so prefer a trailing comment of the previous node. - add_comment( - Trivia::from_token(&token, Relationship::Trailing), - preceding_node, - &mut trivia_index, - ); - } else if let Some(enclosing_node) = enclosing_node { - // TODO(charlie): Prettier puts this later, and uses `Relationship::Dangling`. - add_comment( - Trivia::from_token(&token, Relationship::Trailing), - enclosing_node, - &mut trivia_index, - ); - } else if let Some(following_node) = following_node { - add_comment( - Trivia::from_token(&token, Relationship::Leading), - following_node, - &mut trivia_index, - ); - } else { - unreachable!("Attach token to the ast: {:?}", token); - } - } - TriviaTokenKind::MagicTrailingComma => { - if let Some(enclosing_node) = enclosing_node { - add_comment( - Trivia::from_token(&token, Relationship::Trailing), - enclosing_node, - &mut trivia_index, - ); - } else { - unreachable!("Attach token to the ast: {:?}", token); - } - } - TriviaTokenKind::Parentheses => { - if let Some(enclosed_node) = enclosed_node { - add_comment( - Trivia::from_token(&token, Relationship::Leading), - enclosed_node, - &mut trivia_index, - ); - } else { - unreachable!("Attach token to the ast: {:?}", token); - } - } - } - } - - trivia_index -}