From 07468f8be9f046cbbf11e5b24cdd1840bf6e5bcf Mon Sep 17 00:00:00 2001 From: David Szotten Date: Tue, 1 Aug 2023 07:26:30 +0100 Subject: [PATCH] format ExprJoinedStr (#5932) --- .../fixtures/ruff/expression/joined_string.py | 7 + .../src/expression/expr_constant.rs | 8 +- .../src/expression/expr_joined_str.rs | 15 +- .../src/expression/string.rs | 111 +++++-- ...ility@miscellaneous__debug_visitor.py.snap | 32 +- ...ng_preview_no_string_normalization.py.snap | 4 +- ...aneous__long_strings_flag_disabled.py.snap | 32 +- ...ility@miscellaneous__string_quotes.py.snap | 61 +--- ...y@py_310__pattern_matching_generic.py.snap | 44 +-- ...ty@simple_cases__docstring_preview.py.snap | 51 +-- ...tibility@simple_cases__empty_lines.py.snap | 28 +- ...mpatibility@simple_cases__fmtonoff.py.snap | 9 +- ...ompatibility@simple_cases__fstring.py.snap | 81 ----- ...mpatibility@simple_cases__function.py.snap | 11 +- ...move_newline_after_code_block_open.py.snap | 311 ------------------ ...bility@simple_cases__remove_parens.py.snap | 18 +- ...lity@simple_cases__string_prefixes.py.snap | 120 ------- .../format@expression__joined_string.py.snap | 25 ++ 18 files changed, 221 insertions(+), 747 deletions(-) create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/joined_string.py delete mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fstring.py.snap delete mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_newline_after_code_block_open.py.snap delete mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__string_prefixes.py.snap create mode 100644 crates/ruff_python_formatter/tests/snapshots/format@expression__joined_string.py.snap diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/joined_string.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/joined_string.py new file mode 100644 index 0000000000000..8b6af32d0b587 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/joined_string.py @@ -0,0 +1,7 @@ +( + f'{one}' + f'{two}' +) + + +rf"Not-so-tricky \"quote" diff --git a/crates/ruff_python_formatter/src/expression/expr_constant.rs b/crates/ruff_python_formatter/src/expression/expr_constant.rs index 7c8dd90b4c34c..8594e2640d928 100644 --- a/crates/ruff_python_formatter/src/expression/expr_constant.rs +++ b/crates/ruff_python_formatter/src/expression/expr_constant.rs @@ -7,7 +7,9 @@ use ruff_python_ast::str::is_implicit_concatenation; use crate::expression::number::{FormatComplex, FormatFloat, FormatInt}; use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; -use crate::expression::string::{FormatString, StringLayout, StringPrefix, StringQuotes}; +use crate::expression::string::{ + AnyString, FormatString, StringLayout, StringPrefix, StringQuotes, +}; use crate::prelude::*; use crate::FormatNodeRule; @@ -56,7 +58,9 @@ impl FormatNodeRule for FormatExprConstant { ExprConstantLayout::Default => StringLayout::Default, ExprConstantLayout::String(layout) => layout, }; - FormatString::new(item).with_layout(string_layout).fmt(f) + FormatString::new(&AnyString::Constant(item)) + .with_layout(string_layout) + .fmt(f) } } } diff --git a/crates/ruff_python_formatter/src/expression/expr_joined_str.rs b/crates/ruff_python_formatter/src/expression/expr_joined_str.rs index b3333e65b3c58..e857ea0b30ebf 100644 --- a/crates/ruff_python_formatter/src/expression/expr_joined_str.rs +++ b/crates/ruff_python_formatter/src/expression/expr_joined_str.rs @@ -1,7 +1,9 @@ +use super::string::{AnyString, FormatString}; use crate::context::PyFormatContext; use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; -use crate::{not_yet_implemented_custom_text, FormatNodeRule, PyFormatter}; -use ruff_formatter::{write, Buffer, FormatResult}; +use crate::prelude::*; +use crate::{FormatNodeRule, PyFormatter}; +use ruff_formatter::FormatResult; use ruff_python_ast::node::AnyNodeRef; use ruff_python_ast::ExprJoinedStr; @@ -9,13 +11,8 @@ use ruff_python_ast::ExprJoinedStr; pub struct FormatExprJoinedStr; impl FormatNodeRule for FormatExprJoinedStr { - fn fmt_fields(&self, _item: &ExprJoinedStr, f: &mut PyFormatter) -> FormatResult<()> { - write!( - f, - [not_yet_implemented_custom_text( - r#"f"NOT_YET_IMPLEMENTED_ExprJoinedStr""# - )] - ) + fn fmt_fields(&self, item: &ExprJoinedStr, f: &mut PyFormatter) -> FormatResult<()> { + FormatString::new(&AnyString::JoinedStr(item)).fmt(f) } } diff --git a/crates/ruff_python_formatter/src/expression/string.rs b/crates/ruff_python_formatter/src/expression/string.rs index d6ed59f5ed8e5..b315b8c1a1eeb 100644 --- a/crates/ruff_python_formatter/src/expression/string.rs +++ b/crates/ruff_python_formatter/src/expression/string.rs @@ -1,9 +1,11 @@ use std::borrow::Cow; use bitflags::bitflags; -use ruff_python_ast::{ExprConstant, Ranged}; +use ruff_python_ast::node::AnyNodeRef; +use ruff_python_ast::{self as ast, ExprConstant, ExprJoinedStr, Ranged}; use ruff_python_parser::lexer::{lex_starts_at, LexicalError, LexicalErrorType}; use ruff_python_parser::{Mode, Tok}; +use ruff_source_file::Locator; use ruff_text_size::{TextLen, TextRange, TextSize}; use ruff_formatter::{format_args, write, FormatError}; @@ -13,11 +15,62 @@ use crate::comments::{leading_comments, trailing_comments}; use crate::expression::parentheses::{ in_parentheses_only_group, in_parentheses_only_soft_line_break_or_space, }; +use crate::expression::Expr; use crate::prelude::*; use crate::QuoteStyle; +#[derive(Copy, Clone)] +enum Quoting { + CanChange, + Preserve, +} + +pub(super) enum AnyString<'a> { + Constant(&'a ExprConstant), + JoinedStr(&'a ExprJoinedStr), +} + +impl<'a> AnyString<'a> { + fn quoting(&self, locator: &Locator) -> Quoting { + match self { + Self::Constant(_) => Quoting::CanChange, + Self::JoinedStr(joined_str) => { + if joined_str.values.iter().any(|value| match value { + Expr::FormattedValue(ast::ExprFormattedValue { range, .. }) => { + let string_content = locator.slice(*range); + string_content.contains(['"', '\'']) + } + _ => false, + }) { + Quoting::Preserve + } else { + Quoting::CanChange + } + } + } + } +} + +impl Ranged for AnyString<'_> { + fn range(&self) -> TextRange { + match self { + Self::Constant(expr) => expr.range(), + Self::JoinedStr(expr) => expr.range(), + } + } +} + +impl<'a> From<&AnyString<'a>> for AnyNodeRef<'a> { + fn from(value: &AnyString<'a>) -> Self { + match value { + AnyString::Constant(expr) => AnyNodeRef::ExprConstant(expr), + AnyString::JoinedStr(expr) => AnyNodeRef::ExprJoinedStr(expr), + } + } +} + pub(super) struct FormatString<'a> { - constant: &'a ExprConstant, + string: &'a AnyString<'a>, layout: StringLayout, } @@ -30,10 +83,12 @@ pub enum StringLayout { } impl<'a> FormatString<'a> { - pub(super) fn new(constant: &'a ExprConstant) -> Self { - debug_assert!(constant.value.is_str() || constant.value.is_bytes()); + pub(super) fn new(string: &'a AnyString) -> Self { + if let AnyString::Constant(constant) = string { + debug_assert!(constant.value.is_str() || constant.value.is_bytes()); + } Self { - constant, + string, layout: StringLayout::Default, } } @@ -48,30 +103,33 @@ impl<'a> Format> for FormatString<'a> { fn fmt(&self, f: &mut Formatter>) -> FormatResult<()> { match self.layout { StringLayout::Default => { - let string_range = self.constant.range(); + let string_range = self.string.range(); let string_content = f.context().locator().slice(string_range); if is_implicit_concatenation(string_content) { - in_parentheses_only_group(&FormatStringContinuation::new(self.constant)).fmt(f) + in_parentheses_only_group(&FormatStringContinuation::new(self.string)).fmt(f) } else { - FormatStringPart::new(string_range).fmt(f) + FormatStringPart::new(string_range, self.string.quoting(&f.context().locator())) + .fmt(f) } } StringLayout::ImplicitConcatenatedBinaryLeftSide => { - FormatStringContinuation::new(self.constant).fmt(f) + FormatStringContinuation::new(self.string).fmt(f) } } } } struct FormatStringContinuation<'a> { - constant: &'a ExprConstant, + string: &'a AnyString<'a>, } impl<'a> FormatStringContinuation<'a> { - fn new(constant: &'a ExprConstant) -> Self { - debug_assert!(constant.value.is_str() || constant.value.is_bytes()); - Self { constant } + fn new(string: &'a AnyString<'a>) -> Self { + if let AnyString::Constant(constant) = string { + debug_assert!(constant.value.is_str() || constant.value.is_bytes()); + } + Self { string } } } @@ -79,9 +137,9 @@ impl Format> for FormatStringContinuation<'_> { fn fmt(&self, f: &mut Formatter>) -> FormatResult<()> { let comments = f.context().comments().clone(); let locator = f.context().locator(); - let mut dangling_comments = comments.dangling_comments(self.constant); + let mut dangling_comments = comments.dangling_comments(self.string); - let string_range = self.constant.range(); + let string_range = self.string.range(); let string_content = locator.slice(string_range); // The AST parses implicit concatenation as a single string. @@ -155,7 +213,7 @@ impl Format> for FormatStringContinuation<'_> { joiner.entry(&format_args![ line_suffix_boundary(), leading_comments(leading_part_comments), - FormatStringPart::new(token_range), + FormatStringPart::new(token_range, self.string.quoting(&locator)), trailing_comments(trailing_part_comments) ]); @@ -178,11 +236,15 @@ impl Format> for FormatStringContinuation<'_> { struct FormatStringPart { part_range: TextRange, + quoting: Quoting, } impl FormatStringPart { - const fn new(range: TextRange) -> Self { - Self { part_range: range } + const fn new(range: TextRange, quoting: Quoting) -> Self { + Self { + part_range: range, + quoting, + } } } @@ -204,10 +266,15 @@ impl Format> for FormatStringPart { let raw_content = &string_content[relative_raw_content_range]; let is_raw_string = prefix.is_raw_string(); - let preferred_quotes = if is_raw_string { - preferred_quotes_raw(raw_content, quotes, f.options().quote_style()) - } else { - preferred_quotes(raw_content, quotes, f.options().quote_style()) + let preferred_quotes = match self.quoting { + Quoting::Preserve => quotes, + Quoting::CanChange => { + if is_raw_string { + preferred_quotes_raw(raw_content, quotes, f.options().quote_style()) + } else { + preferred_quotes(raw_content, quotes, f.options().quote_style()) + } + } }; write!(f, [prefix, preferred_quotes])?; diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__debug_visitor.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__debug_visitor.py.snap index 2773e587be52a..5d7941640f647 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__debug_visitor.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__debug_visitor.py.snap @@ -44,7 +44,7 @@ class DebugVisitor(Visitor[T]): ```diff --- Black +++ Ruff -@@ -3,24 +3,29 @@ +@@ -3,24 +3,24 @@ tree_depth: int = 0 def visit_default(self, node: LN) -> Iterator[T]: @@ -53,30 +53,25 @@ class DebugVisitor(Visitor[T]): if isinstance(node, Node): _type = type_repr(node.type) - out(f'{indent}{_type}', fg='yellow') -+ out(f"NOT_YET_IMPLEMENTED_ExprJoinedStr", fg="yellow") ++ out(f"{indent}{_type}", fg="yellow") self.tree_depth += 1 for child in node.children: yield from self.visit(child) self.tree_depth -= 1 - out(f'{indent}/{_type}', fg='yellow', bold=False) -+ out(f"NOT_YET_IMPLEMENTED_ExprJoinedStr", fg="yellow", bold=False) ++ out(f"{indent}/{_type}", fg="yellow", bold=False) else: _type = token.tok_name.get(node.type, str(node.type)) - out(f'{indent}{_type}', fg='blue', nl=False) -+ out(f"NOT_YET_IMPLEMENTED_ExprJoinedStr", fg="blue", nl=False) ++ out(f"{indent}{_type}", fg="blue", nl=False) if node.prefix: # We don't have to handle prefixes for `Node` objects since # that delegates to the first child anyway. - out(f' {node.prefix!r}', fg='green', bold=False, nl=False) - out(f' {node.value!r}', fg='blue', bold=False) -+ out( -+ f"NOT_YET_IMPLEMENTED_ExprJoinedStr", -+ fg="green", -+ bold=False, -+ nl=False, -+ ) -+ out(f"NOT_YET_IMPLEMENTED_ExprJoinedStr", fg="blue", bold=False) ++ out(f" {node.prefix!r}", fg="green", bold=False, nl=False) ++ out(f" {node.value!r}", fg="blue", bold=False) @classmethod def show(cls, code: str) -> None: @@ -93,26 +88,21 @@ class DebugVisitor(Visitor[T]): indent = " " * (2 * self.tree_depth) if isinstance(node, Node): _type = type_repr(node.type) - out(f"NOT_YET_IMPLEMENTED_ExprJoinedStr", fg="yellow") + out(f"{indent}{_type}", fg="yellow") self.tree_depth += 1 for child in node.children: yield from self.visit(child) self.tree_depth -= 1 - out(f"NOT_YET_IMPLEMENTED_ExprJoinedStr", fg="yellow", bold=False) + out(f"{indent}/{_type}", fg="yellow", bold=False) else: _type = token.tok_name.get(node.type, str(node.type)) - out(f"NOT_YET_IMPLEMENTED_ExprJoinedStr", fg="blue", nl=False) + out(f"{indent}{_type}", fg="blue", nl=False) if node.prefix: # We don't have to handle prefixes for `Node` objects since # that delegates to the first child anyway. - out( - f"NOT_YET_IMPLEMENTED_ExprJoinedStr", - fg="green", - bold=False, - nl=False, - ) - out(f"NOT_YET_IMPLEMENTED_ExprJoinedStr", fg="blue", bold=False) + out(f" {node.prefix!r}", fg="green", bold=False, nl=False) + out(f" {node.value!r}", fg="blue", bold=False) @classmethod def show(cls, code: str) -> None: diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__docstring_preview_no_string_normalization.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__docstring_preview_no_string_normalization.py.snap index 0a617572c9559..4503b0dd0052c 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__docstring_preview_no_string_normalization.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__docstring_preview_no_string_normalization.py.snap @@ -27,7 +27,7 @@ def do_not_touch_this_prefix3(): def do_not_touch_this_prefix2(): - FR'There was a bug where docstring prefixes would be normalized even with -S.' -+ f"NOT_YET_IMPLEMENTED_ExprJoinedStr" ++ Rf"There was a bug where docstring prefixes would be normalized even with -S." def do_not_touch_this_prefix3(): @@ -43,7 +43,7 @@ def do_not_touch_this_prefix(): def do_not_touch_this_prefix2(): - f"NOT_YET_IMPLEMENTED_ExprJoinedStr" + Rf"There was a bug where docstring prefixes would be normalized even with -S." def do_not_touch_this_prefix3(): diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__long_strings_flag_disabled.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__long_strings_flag_disabled.py.snap index 2a5343cdea2c0..6e2a6ac805316 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__long_strings_flag_disabled.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__long_strings_flag_disabled.py.snap @@ -304,19 +304,23 @@ long_unmergable_string_with_pragma = ( ```diff --- Black +++ Ruff -@@ -143,9 +143,9 @@ +@@ -143,9 +143,13 @@ ) ) -fstring = f"f-strings definitely make things more {difficult} than they need to be for {{black}}. But boy they sure are handy. The problem is that some lines will need to have the 'f' whereas others do not. This {line}, for example, needs one." -+fstring = f"NOT_YET_IMPLEMENTED_ExprJoinedStr" ++fstring = ( ++ f"f-strings definitely make things more {difficult} than they need to be for {{black}}. But boy they sure are handy. The problem is that some lines will need to have the 'f' whereas others do not. This {line}, for example, needs one." ++) -fstring_with_no_fexprs = f"Some regular string that needs to get split certainly but is NOT an fstring by any means whatsoever." -+fstring_with_no_fexprs = f"NOT_YET_IMPLEMENTED_ExprJoinedStr" ++fstring_with_no_fexprs = ( ++ f"Some regular string that needs to get split certainly but is NOT an fstring by any means whatsoever." ++) comment_string = "Long lines with inline comments should have their comments appended to the reformatted string's enclosing right parentheses." # This comment gets thrown to the top. -@@ -165,13 +165,9 @@ +@@ -165,13 +169,9 @@ triple_quote_string = """This is a really really really long triple quote string assignment and it should not be touched.""" @@ -332,7 +336,7 @@ long_unmergable_string_with_pragma = ( "formatting" ) -@@ -221,8 +217,8 @@ +@@ -221,8 +221,8 @@ func_with_bad_comma( ( "This is a really long string argument to a function that has a trailing comma" @@ -343,12 +347,14 @@ long_unmergable_string_with_pragma = ( ) func_with_bad_parens_that_wont_fit_in_one_line( -@@ -274,7 +270,7 @@ +@@ -274,7 +274,9 @@ yield "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three." -x = f"This is a {{really}} long string that needs to be split without a doubt (i.e. most definitely). In short, this {string} that can't possibly be {{expected}} to fit all together on one line. In {fact} it may even take up three or more lines... like four or five... but probably just four." -+x = f"NOT_YET_IMPLEMENTED_ExprJoinedStr" ++x = ( ++ f"This is a {{really}} long string that needs to be split without a doubt (i.e. most definitely). In short, this {string} that can't possibly be {{expected}} to fit all together on one line. In {fact} it may even take up three or more lines... like four or five... but probably just four." ++) long_unmergable_string_with_pragma = ( "This is a really long string that can't be merged because it has a likely pragma at the end" # type: ignore @@ -502,9 +508,13 @@ old_fmt_string3 = ( ) ) -fstring = f"NOT_YET_IMPLEMENTED_ExprJoinedStr" +fstring = ( + f"f-strings definitely make things more {difficult} than they need to be for {{black}}. But boy they sure are handy. The problem is that some lines will need to have the 'f' whereas others do not. This {line}, for example, needs one." +) -fstring_with_no_fexprs = f"NOT_YET_IMPLEMENTED_ExprJoinedStr" +fstring_with_no_fexprs = ( + f"Some regular string that needs to get split certainly but is NOT an fstring by any means whatsoever." +) comment_string = "Long lines with inline comments should have their comments appended to the reformatted string's enclosing right parentheses." # This comment gets thrown to the top. @@ -629,7 +639,9 @@ def foo(): yield "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three." -x = f"NOT_YET_IMPLEMENTED_ExprJoinedStr" +x = ( + f"This is a {{really}} long string that needs to be split without a doubt (i.e. most definitely). In short, this {string} that can't possibly be {{expected}} to fit all together on one line. In {fact} it may even take up three or more lines... like four or five... but probably just four." +) long_unmergable_string_with_pragma = ( "This is a really long string that can't be merged because it has a likely pragma at the end" # type: ignore diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__string_quotes.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__string_quotes.py.snap index 9473281ef2a44..022b30a5a30a9 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__string_quotes.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__string_quotes.py.snap @@ -69,25 +69,11 @@ f"\"{a}\"{'hello' * b}\"{c}\"" ```diff --- Black +++ Ruff -@@ -15,16 +15,21 @@ - """Here's a " """ - """Just a normal triple - quote""" --f"just a normal {f} string" --f"""This is a triple-quoted {f}-string""" --f'MOAR {" ".join([])}' --f"MOAR {' '.join([])}" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" - r"raw string ftw" - r"Date d\'expiration:(.*)" +@@ -24,7 +24,12 @@ r'Tricky "quote' r"Not-so-tricky \"quote" --rf"{yay}" + rf"{yay}" -"\nThe \"quick\"\nbrown fox\njumps over\nthe 'lazy' dog.\n" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" +"\n\ +The \"quick\"\n\ +brown fox\n\ @@ -97,27 +83,6 @@ f"\"{a}\"{'hello' * b}\"{c}\"" re.compile(r'[\\"]') "x = ''; y = \"\"" "x = '''; y = \"\"" -@@ -39,14 +44,14 @@ - '\\""' - "\\''" - "Lots of \\\\\\\\'quotes'" --f'{y * " "} \'{z}\'' --f"{{y * \" \"}} '{z}'" --f'\'{z}\' {y * " "}' --f"{y * x} '{z}'" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" - "'{z}' {y * \" \"}" - "{y * x} '{z}'" - - # We must bail out if changing the quotes would introduce backslashes in f-string - # expressions. xref: https://github.com/psf/black/issues/2348 --f"\"{b}\"{' ' * (long-len(b)+1)}: \"{sts}\",\n" --f"\"{a}\"{'hello' * b}\"{c}\"" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" ``` ## Ruff Output @@ -140,15 +105,15 @@ f"\"{a}\"{'hello' * b}\"{c}\"" """Here's a " """ """Just a normal triple quote""" -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" +f"just a normal {f} string" +f"""This is a triple-quoted {f}-string""" +f'MOAR {" ".join([])}' +f"MOAR {' '.join([])}" r"raw string ftw" r"Date d\'expiration:(.*)" r'Tricky "quote' r"Not-so-tricky \"quote" -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" +rf"{yay}" "\n\ The \"quick\"\n\ brown fox\n\ @@ -169,17 +134,17 @@ re.compile(r'[\\"]') '\\""' "\\''" "Lots of \\\\\\\\'quotes'" -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" +f'{y * " "} \'{z}\'' +f"{{y * \" \"}} '{z}'" +f'\'{z}\' {y * " "}' +f"{y * x} '{z}'" "'{z}' {y * \" \"}" "{y * x} '{z}'" # We must bail out if changing the quotes would introduce backslashes in f-string # expressions. xref: https://github.com/psf/black/issues/2348 -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" +f"\"{b}\"{' ' * (long-len(b)+1)}: \"{sts}\",\n" +f"\"{a}\"{'hello' * b}\"{c}\"" ``` ## Black Output diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_generic.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_generic.py.snap index 8e653596d3bb3..6049e7897bebb 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_generic.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_generic.py.snap @@ -119,21 +119,6 @@ with match() as match: ```diff --- Black +++ Ruff -@@ -1,12 +1,12 @@ - re.match() - match = a - with match() as match: -- match = f"{match}" -+ match = f"NOT_YET_IMPLEMENTED_ExprJoinedStr" - - re.match() - match = a - with match() as match: -- match = f"{match}" -+ match = f"NOT_YET_IMPLEMENTED_ExprJoinedStr" - - - def get_grammars(target_versions: Set[TargetVersion]) -> List[Grammar]: @@ -23,11 +23,7 @@ pygram.python_grammar, ] @@ -147,12 +132,7 @@ with match() as match: if all(version.is_python2() for version in target_versions): # Python 2-only code, so try Python 2 grammars. -@@ -41,13 +37,11 @@ - re.match() - match = a - with match() as match: -- match = f"{match}" -+ match = f"NOT_YET_IMPLEMENTED_ExprJoinedStr" +@@ -45,9 +41,7 @@ def test_patma_139(self): x = False @@ -182,18 +162,6 @@ with match() as match: # At least one of the above branches must have been taken, because every Python # version has exactly one of the two 'ASYNC_*' flags -@@ -99,9 +89,9 @@ - re.match() - match = a - with match() as match: -- match = f"{match}" -+ match = f"NOT_YET_IMPLEMENTED_ExprJoinedStr" - - re.match() - match = a - with match() as match: -- match = f"{match}" -+ match = f"NOT_YET_IMPLEMENTED_ExprJoinedStr" ``` ## Ruff Output @@ -202,12 +170,12 @@ with match() as match: re.match() match = a with match() as match: - match = f"NOT_YET_IMPLEMENTED_ExprJoinedStr" + match = f"{match}" re.match() match = a with match() as match: - match = f"NOT_YET_IMPLEMENTED_ExprJoinedStr" + match = f"{match}" def get_grammars(target_versions: Set[TargetVersion]) -> List[Grammar]: @@ -238,7 +206,7 @@ def get_grammars(target_versions: Set[TargetVersion]) -> List[Grammar]: re.match() match = a with match() as match: - match = f"NOT_YET_IMPLEMENTED_ExprJoinedStr" + match = f"{match}" def test_patma_139(self): x = False @@ -290,12 +258,12 @@ def lib2to3_parse(src_txt: str, target_versions: Iterable[TargetVersion] = ()) - re.match() match = a with match() as match: - match = f"NOT_YET_IMPLEMENTED_ExprJoinedStr" + match = f"{match}" re.match() match = a with match() as match: - match = f"NOT_YET_IMPLEMENTED_ExprJoinedStr" + match = f"{match}" ``` ## Black Output diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__docstring_preview.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__docstring_preview.py.snap index d297799051528..427408a63cc7d 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__docstring_preview.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__docstring_preview.py.snap @@ -62,7 +62,7 @@ def single_quote_docstring_over_line_limit2(): ```diff --- Black +++ Ruff -@@ -1,9 +1,10 @@ +@@ -1,9 +1,11 @@ def docstring_almost_at_line_limit(): - """long docstring.................................................................""" + """long docstring................................................................. @@ -71,42 +71,11 @@ def single_quote_docstring_over_line_limit2(): def docstring_almost_at_line_limit_with_prefix(): - f"""long docstring................................................................""" -+ f"NOT_YET_IMPLEMENTED_ExprJoinedStr" ++ f"""long docstring................................................................ ++ """ def mulitline_docstring_almost_at_line_limit(): -@@ -14,10 +15,7 @@ - - - def mulitline_docstring_almost_at_line_limit_with_prefix(): -- f"""long docstring................................................................ -- -- .................................................................................. -- """ -+ f"NOT_YET_IMPLEMENTED_ExprJoinedStr" - - - def docstring_at_line_limit(): -@@ -25,7 +23,7 @@ - - - def docstring_at_line_limit_with_prefix(): -- f"""long docstring...............................................................""" -+ f"NOT_YET_IMPLEMENTED_ExprJoinedStr" - - - def multiline_docstring_at_line_limit(): -@@ -35,9 +33,7 @@ - - - def multiline_docstring_at_line_limit_with_prefix(): -- f"""first line---------------------------------------------------------------------- -- -- second line----------------------------------------------------------------------""" -+ f"NOT_YET_IMPLEMENTED_ExprJoinedStr" - - - def single_quote_docstring_over_line_limit(): ``` ## Ruff Output @@ -118,7 +87,8 @@ def docstring_almost_at_line_limit(): def docstring_almost_at_line_limit_with_prefix(): - f"NOT_YET_IMPLEMENTED_ExprJoinedStr" + f"""long docstring................................................................ + """ def mulitline_docstring_almost_at_line_limit(): @@ -129,7 +99,10 @@ def mulitline_docstring_almost_at_line_limit(): def mulitline_docstring_almost_at_line_limit_with_prefix(): - f"NOT_YET_IMPLEMENTED_ExprJoinedStr" + f"""long docstring................................................................ + + .................................................................................. + """ def docstring_at_line_limit(): @@ -137,7 +110,7 @@ def docstring_at_line_limit(): def docstring_at_line_limit_with_prefix(): - f"NOT_YET_IMPLEMENTED_ExprJoinedStr" + f"""long docstring...............................................................""" def multiline_docstring_at_line_limit(): @@ -147,7 +120,9 @@ def multiline_docstring_at_line_limit(): def multiline_docstring_at_line_limit_with_prefix(): - f"NOT_YET_IMPLEMENTED_ExprJoinedStr" + f"""first line---------------------------------------------------------------------- + + second line----------------------------------------------------------------------""" def single_quote_docstring_over_line_limit(): diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__empty_lines.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__empty_lines.py.snap index 10d5e1ee87270..72e8d2306df40 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__empty_lines.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__empty_lines.py.snap @@ -104,32 +104,14 @@ def g(): ```diff --- Black +++ Ruff -@@ -16,7 +16,7 @@ - if t == token.COMMENT: # another trailing comment - return DOUBLESPACE - -- assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}" -+ assert p is not None, f"NOT_YET_IMPLEMENTED_ExprJoinedStr" - - prev = leaf.prev_sibling - if not prev: -@@ -48,7 +48,6 @@ - ############################################################################### +@@ -49,7 +49,6 @@ # SECTION BECAUSE SECTIONS ############################################################################### -- +- def g(): NO = "" -@@ -67,7 +66,7 @@ - return DOUBLESPACE - - # Another comment because more comments -- assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}" -+ assert p is not None, f"NOT_YET_IMPLEMENTED_ExprJoinedStr" - - prev = leaf.prev_sibling - if not prev: + SPACE = " " ``` ## Ruff Output @@ -153,7 +135,7 @@ def f(): if t == token.COMMENT: # another trailing comment return DOUBLESPACE - assert p is not None, f"NOT_YET_IMPLEMENTED_ExprJoinedStr" + assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}" prev = leaf.prev_sibling if not prev: @@ -203,7 +185,7 @@ def g(): return DOUBLESPACE # Another comment because more comments - assert p is not None, f"NOT_YET_IMPLEMENTED_ExprJoinedStr" + assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}" prev = leaf.prev_sibling if not prev: diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff.py.snap index 1cc7c53d39690..88e2db7a36ea8 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff.py.snap @@ -198,7 +198,7 @@ d={'a':1, ```diff --- Black +++ Ruff -@@ -6,10 +6,10 @@ +@@ -6,8 +6,8 @@ from library import some_connection, some_decorator # fmt: off @@ -207,11 +207,8 @@ d={'a':1, +from third_party import X, Y, Z + # fmt: on --f"trigger 3.6 mode" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" + f"trigger 3.6 mode" # Comment 1 - - # Comment 2 @@ -17,26 +17,40 @@ # fmt: off @@ -403,7 +400,7 @@ from library import some_connection, some_decorator from third_party import X, Y, Z # fmt: on -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" +f"trigger 3.6 mode" # Comment 1 # Comment 2 diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fstring.py.snap deleted file mode 100644 index 7282dc5589730..0000000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fstring.py.snap +++ /dev/null @@ -1,81 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fstring.py ---- -## Input - -```py -f"f-string without formatted values is just a string" -f"{{NOT a formatted value}}" -f"{{NOT 'a' \"formatted\" \"value\"}}" -f"some f-string with {a} {few():.2f} {formatted.values!r}" -f'some f-string with {a} {few(""):.2f} {formatted.values!r}' -f"{f'''{'nested'} inner'''} outer" -f"\"{f'{nested} inner'}\" outer" -f"space between opening braces: { {a for a in (1, 2, 3)}}" -f'Hello \'{tricky + "example"}\'' -f"Tried directories {str(rootdirs)} \ -but none started with prefix {parentdir_prefix}" -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -1,11 +1,10 @@ --f"f-string without formatted values is just a string" --f"{{NOT a formatted value}}" --f'{{NOT \'a\' "formatted" "value"}}' --f"some f-string with {a} {few():.2f} {formatted.values!r}" --f'some f-string with {a} {few(""):.2f} {formatted.values!r}' --f"{f'''{'nested'} inner'''} outer" --f"\"{f'{nested} inner'}\" outer" --f"space between opening braces: { {a for a in (1, 2, 3)}}" --f'Hello \'{tricky + "example"}\'' --f"Tried directories {str(rootdirs)} \ --but none started with prefix {parentdir_prefix}" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -``` - -## Ruff Output - -```py -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" -``` - -## Black Output - -```py -f"f-string without formatted values is just a string" -f"{{NOT a formatted value}}" -f'{{NOT \'a\' "formatted" "value"}}' -f"some f-string with {a} {few():.2f} {formatted.values!r}" -f'some f-string with {a} {few(""):.2f} {formatted.values!r}' -f"{f'''{'nested'} inner'''} outer" -f"\"{f'{nested} inner'}\" outer" -f"space between opening braces: { {a for a in (1, 2, 3)}}" -f'Hello \'{tricky + "example"}\'' -f"Tried directories {str(rootdirs)} \ -but none started with prefix {parentdir_prefix}" -``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__function.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__function.py.snap index 9e903ec50c900..0e3bfa106cfe5 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__function.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__function.py.snap @@ -107,15 +107,6 @@ def __await__(): return (yield) ```diff --- Black +++ Ruff -@@ -6,7 +6,7 @@ - - from library import some_connection, some_decorator - --f"trigger 3.6 mode" -+f"NOT_YET_IMPLEMENTED_ExprJoinedStr" - - - def func_no_args(): @@ -65,18 +65,14 @@ def spaces2(result=_core.Value(None)): @@ -153,7 +144,7 @@ from third_party import X, Y, Z from library import some_connection, some_decorator -f"NOT_YET_IMPLEMENTED_ExprJoinedStr" +f"trigger 3.6 mode" def func_no_args(): diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_newline_after_code_block_open.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_newline_after_code_block_open.py.snap deleted file mode 100644 index a47f6ddb568e9..0000000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_newline_after_code_block_open.py.snap +++ /dev/null @@ -1,311 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -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 -@@ -27,16 +27,16 @@ - - - for i in range(5): -- print(f"{i}) The line above me should be removed!") -+ print(f"NOT_YET_IMPLEMENTED_ExprJoinedStr") - - - for i in range(5): -- print(f"{i}) The lines above me should be removed!") -+ print(f"NOT_YET_IMPLEMENTED_ExprJoinedStr") - - - for i in range(5): - for j in range(7): -- print(f"{i}) The lines above me should be removed!") -+ print(f"NOT_YET_IMPLEMENTED_ExprJoinedStr") - - - if random.randint(0, 3) == 0: -``` - -## 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"NOT_YET_IMPLEMENTED_ExprJoinedStr") - - -for i in range(5): - print(f"NOT_YET_IMPLEMENTED_ExprJoinedStr") - - -for i in range(5): - for j in range(7): - print(f"NOT_YET_IMPLEMENTED_ExprJoinedStr") - - -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/tests/snapshots/black_compatibility@simple_cases__remove_parens.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_parens.py.snap index fa89bfa512903..9cb91d4c09e58 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_parens.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_parens.py.snap @@ -67,18 +67,20 @@ def example8(): ```diff --- Black +++ Ruff -@@ -10,9 +10,7 @@ - while True: +@@ -11,8 +11,10 @@ try: if report_host: -- data = ( + data = ( - f"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - ).encode() -+ data = (f"NOT_YET_IMPLEMENTED_ExprJoinedStr").encode() ++ ( ++ f"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ++ ).encode() ++ ) except Exception as e: pass -@@ -30,15 +28,11 @@ +@@ -30,15 +32,11 @@ def example2(): @@ -113,7 +115,11 @@ async def show_status(): while True: try: if report_host: - data = (f"NOT_YET_IMPLEMENTED_ExprJoinedStr").encode() + data = ( + ( + f"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + ).encode() + ) except Exception as e: pass diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__string_prefixes.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__string_prefixes.py.snap deleted file mode 100644 index a312eb7488a2d..0000000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__string_prefixes.py.snap +++ /dev/null @@ -1,120 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/string_prefixes.py ---- -## Input - -```py -#!/usr/bin/env python3 - -name = "Łukasz" -(f"hello {name}", F"hello {name}") -(b"", B"") -(u"", U"") -(r"", R"") - -(rf"", fr"", Rf"", fR"", rF"", Fr"", RF"", FR"") -(rb"", br"", Rb"", bR"", rB"", Br"", RB"", BR"") - - -def docstring_singleline(): - R"""2020 was one hell of a year. The good news is that we were able to""" - - -def docstring_multiline(): - R""" - clear out all of the issues opened in that time :p - """ -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -1,12 +1,21 @@ - #!/usr/bin/env python3 - - name = "Łukasz" --(f"hello {name}", f"hello {name}") -+(f"NOT_YET_IMPLEMENTED_ExprJoinedStr", f"NOT_YET_IMPLEMENTED_ExprJoinedStr") - (b"", b"") - ("", "") - (r"", R"") - --(rf"", rf"", Rf"", Rf"", rf"", rf"", Rf"", Rf"") -+( -+ f"NOT_YET_IMPLEMENTED_ExprJoinedStr", -+ f"NOT_YET_IMPLEMENTED_ExprJoinedStr", -+ f"NOT_YET_IMPLEMENTED_ExprJoinedStr", -+ f"NOT_YET_IMPLEMENTED_ExprJoinedStr", -+ f"NOT_YET_IMPLEMENTED_ExprJoinedStr", -+ f"NOT_YET_IMPLEMENTED_ExprJoinedStr", -+ f"NOT_YET_IMPLEMENTED_ExprJoinedStr", -+ f"NOT_YET_IMPLEMENTED_ExprJoinedStr", -+) - (rb"", rb"", Rb"", Rb"", rb"", rb"", Rb"", Rb"") - - -``` - -## Ruff Output - -```py -#!/usr/bin/env python3 - -name = "Łukasz" -(f"NOT_YET_IMPLEMENTED_ExprJoinedStr", f"NOT_YET_IMPLEMENTED_ExprJoinedStr") -(b"", b"") -("", "") -(r"", R"") - -( - f"NOT_YET_IMPLEMENTED_ExprJoinedStr", - f"NOT_YET_IMPLEMENTED_ExprJoinedStr", - f"NOT_YET_IMPLEMENTED_ExprJoinedStr", - f"NOT_YET_IMPLEMENTED_ExprJoinedStr", - f"NOT_YET_IMPLEMENTED_ExprJoinedStr", - f"NOT_YET_IMPLEMENTED_ExprJoinedStr", - f"NOT_YET_IMPLEMENTED_ExprJoinedStr", - f"NOT_YET_IMPLEMENTED_ExprJoinedStr", -) -(rb"", rb"", Rb"", Rb"", rb"", rb"", Rb"", Rb"") - - -def docstring_singleline(): - R"""2020 was one hell of a year. The good news is that we were able to""" - - -def docstring_multiline(): - R""" - clear out all of the issues opened in that time :p - """ -``` - -## Black Output - -```py -#!/usr/bin/env python3 - -name = "Łukasz" -(f"hello {name}", f"hello {name}") -(b"", b"") -("", "") -(r"", R"") - -(rf"", rf"", Rf"", Rf"", rf"", rf"", Rf"", Rf"") -(rb"", rb"", Rb"", Rb"", rb"", rb"", Rb"", Rb"") - - -def docstring_singleline(): - R"""2020 was one hell of a year. The good news is that we were able to""" - - -def docstring_multiline(): - R""" - clear out all of the issues opened in that time :p - """ -``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__joined_string.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__joined_string.py.snap new file mode 100644 index 0000000000000..d9f8f4313d993 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__joined_string.py.snap @@ -0,0 +1,25 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/joined_string.py +--- +## Input +```py +( + f'{one}' + f'{two}' +) + + +rf"Not-so-tricky \"quote" +``` + +## Output +```py +(f"{one}" f"{two}") + + +rf'Not-so-tricky "quote' +``` + + +