From d720c51a9f3222eed753f5fb560659a1398b0ce9 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Wed, 27 Nov 2024 10:30:23 +0530 Subject: [PATCH] Do not consider f-strings with escaped newlines as multiline --- .../test/fixtures/ruff/expression/binary.py | 7 +++ .../join_implicit_concatenated_string.py | 21 +++++++++ .../ruff_python_formatter/src/string/mod.rs | 16 +++++-- .../format@expression__binary.py.snap | 39 +++++++++++++++- ..._join_implicit_concatenated_string.py.snap | 46 ++++++++++++++++++- 5 files changed, 124 insertions(+), 5 deletions(-) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/binary.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/binary.py index 818dca0bc76ac..8b27e3cd9f1a4 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/binary.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/binary.py @@ -424,3 +424,10 @@ expression } bbbbbbbbbbbbbbbbbbbbbbbb" + ( yyyyyyyyyyyyyy + zzzzzzzzzzz ) + +# This is not a multiline f-string, but the expression is too long so it should be +# wrapped in parentheses. +f"hellooooooooooooooooooooooo \ + worlddddddddddddddddddddddddddddddddd" + (aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb) +aaaaaaaaaaa = f"hellooooooooooooooooooooooo \ + worlddddddddddddddddddddddddddddddddd" + (aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string.py index a2548aa5524b3..184e727a26441 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string.py @@ -326,3 +326,24 @@ def implicit_with_comment(): "a" f'{1=: "abcd \'\'}' f'{1=: "abcd \'\'}' "a" f'{1=: "abcd \'\'}' f"{1=: 'abcd \"\"}" + +# These strings contains escaped newline characters and should be joined, they are +# not multiline strings. +f"aaaaaaaaaaaaaaaa \ + bbbbbbbbbbb" "cccccccccccccc \ + ddddddddddddddddddd" +b"aaaaaaaaaaaaaaaa \ + bbbbbbbbbbb" b"cccccccccccccc \ + ddddddddddddddddddd" +f"aaaaaaaaaaaaaaaa \ + bbbbbbbbbbb" "cccccccccccccc \ + ddddddddddddddddddd" # comment 1 +(f"aaaaaaaaaaaaaaaa \ + bbbbbbbbbbb" "cccccccccccccc \ + ddddddddddddddddddd") # comment 2 +( + f"aaaaaaaaaaaaaaaa \ + bbbbbbbbbbb" # comment 3 + "cccccccccccccc \ + ddddddddddddddddddd" # comment 4 +) diff --git a/crates/ruff_python_formatter/src/string/mod.rs b/crates/ruff_python_formatter/src/string/mod.rs index 071f5f45c9ed3..9f76aee3c7a03 100644 --- a/crates/ruff_python_formatter/src/string/mod.rs +++ b/crates/ruff_python_formatter/src/string/mod.rs @@ -113,10 +113,12 @@ impl StringLikeExtensions for ast::StringLike<'_> { fn contains_line_break_or_comments( elements: &ast::FStringElements, context: &PyFormatContext, + is_triple_quoted: bool, ) -> bool { elements.iter().any(|element| match element { ast::FStringElement::Literal(literal) => { - context.source().contains_line_break(literal.range()) + is_triple_quoted + && context.source().contains_line_break(literal.range()) } ast::FStringElement::Expression(expression) => { // Expressions containing comments can't be joined. @@ -132,7 +134,11 @@ impl StringLikeExtensions for ast::StringLike<'_> { // ``` context.comments().contains_comments(expression.into()) || expression.format_spec.as_deref().is_some_and(|spec| { - contains_line_break_or_comments(&spec.elements, context) + contains_line_break_or_comments( + &spec.elements, + context, + is_triple_quoted, + ) }) || expression.debug_text.as_ref().is_some_and(|debug_text| { memchr2(b'\n', b'\r', debug_text.leading.as_bytes()).is_some() @@ -144,7 +150,11 @@ impl StringLikeExtensions for ast::StringLike<'_> { } if is_f_string_formatting_enabled(context) { - contains_line_break_or_comments(&f_string.elements, context) + contains_line_break_or_comments( + &f_string.elements, + context, + f_string.flags.is_triple_quoted(), + ) } else { context.source().contains_line_break(f_string.range()) } diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap index 5f2072ff10794..8fa48c9f0ac8f 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap @@ -430,6 +430,13 @@ xxxxxxxxxxxxxxxx = f"aaaaaaaaaaaaaaaaaaaaa { expression } bbbbbbbbbbbbbbbbbbbbbbbb" + ( yyyyyyyyyyyyyy + zzzzzzzzzzz ) + +# This is not a multiline f-string, but the expression is too long so it should be +# wrapped in parentheses. +f"hellooooooooooooooooooooooo \ + worlddddddddddddddddddddddddddddddddd" + (aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb) +aaaaaaaaaaa = f"hellooooooooooooooooooooooo \ + worlddddddddddddddddddddddddddddddddd" + (aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb) ``` ## Output @@ -906,6 +913,17 @@ if True: # This f-string should be flattened xxxxxxxxxxxxxxxx = f"aaaaaaaaaaaaaaaaaaaaa { expression } bbbbbbbbbbbbbbbbbbbbbbbb" + (yyyyyyyyyyyyyy + zzzzzzzzzzz) + +# This is not a multiline f-string, but the expression is too long so it should be +# wrapped in parentheses. +f"hellooooooooooooooooooooooo \ + worlddddddddddddddddddddddddddddddddd" + ( + aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb +) +aaaaaaaaaaa = f"hellooooooooooooooooooooooo \ + worlddddddddddddddddddddddddddddddddd" + ( + aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb +) ``` @@ -913,7 +931,7 @@ xxxxxxxxxxxxxxxx = f"aaaaaaaaaaaaaaaaaaaaa { ```diff --- Stable +++ Preview -@@ -468,5 +468,6 @@ +@@ -468,16 +468,19 @@ ) # This f-string should be flattened @@ -922,4 +940,23 @@ xxxxxxxxxxxxxxxx = f"aaaaaaaaaaaaaaaaaaaaa { +xxxxxxxxxxxxxxxx = f"aaaaaaaaaaaaaaaaaaaaa {expression} bbbbbbbbbbbbbbbbbbbbbbbb" + ( + yyyyyyyyyyyyyy + zzzzzzzzzzz +) + + # This is not a multiline f-string, but the expression is too long so it should be + # wrapped in parentheses. +-f"hellooooooooooooooooooooooo \ +- worlddddddddddddddddddddddddddddddddd" + ( +- aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb ++( ++ f"hellooooooooooooooooooooooo \ ++ worlddddddddddddddddddddddddddddddddd" ++ + (aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb) + ) +-aaaaaaaaaaa = f"hellooooooooooooooooooooooo \ +- worlddddddddddddddddddddddddddddddddd" + ( +- aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb ++aaaaaaaaaaa = ( ++ f"hellooooooooooooooooooooooo \ ++ worlddddddddddddddddddddddddddddddddd" ++ + (aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb) + ) ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string.py.snap index 33a2c90cb4229..ff4ba5eb5e548 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string.py.snap @@ -1,7 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string.py -snapshot_kind: text --- ## Input ```python @@ -333,6 +332,27 @@ assert False, +"Implicit concatenated string" "uses {} layout on {} format".form "a" f'{1=: "abcd \'\'}' f'{1=: "abcd \'\'}' "a" f'{1=: "abcd \'\'}' f"{1=: 'abcd \"\"}" + +# These strings contains escaped newline characters and should be joined, they are +# not multiline strings. +f"aaaaaaaaaaaaaaaa \ + bbbbbbbbbbb" "cccccccccccccc \ + ddddddddddddddddddd" +b"aaaaaaaaaaaaaaaa \ + bbbbbbbbbbb" b"cccccccccccccc \ + ddddddddddddddddddd" +f"aaaaaaaaaaaaaaaa \ + bbbbbbbbbbb" "cccccccccccccc \ + ddddddddddddddddddd" # comment 1 +(f"aaaaaaaaaaaaaaaa \ + bbbbbbbbbbb" "cccccccccccccc \ + ddddddddddddddddddd") # comment 2 +( + f"aaaaaaaaaaaaaaaa \ + bbbbbbbbbbb" # comment 3 + "cccccccccccccc \ + ddddddddddddddddddd" # comment 4 +) ``` ## Outputs @@ -747,4 +767,28 @@ assert False, +"Implicit concatenated stringuses {} layout on {} format".format( f'a{1=: "abcd \'\'}' f'{1=: "abcd \'\'}a' f'{1=: "abcd \'\'}' f"{1=: 'abcd \"\"}" + +# These strings contains escaped newline characters and should be joined, they are +# not multiline strings. +f"aaaaaaaaaaaaaaaa \ + bbbbbbbbbbbcccccccccccccc \ + ddddddddddddddddddd" +b"aaaaaaaaaaaaaaaa \ + bbbbbbbbbbbcccccccccccccc \ + ddddddddddddddddddd" +f"aaaaaaaaaaaaaaaa \ + bbbbbbbbbbbcccccccccccccc \ + ddddddddddddddddddd" # comment 1 +( + f"aaaaaaaaaaaaaaaa \ + bbbbbbbbbbb" + "cccccccccccccc \ + ddddddddddddddddddd" +) # comment 2 +( + f"aaaaaaaaaaaaaaaa \ + bbbbbbbbbbb" # comment 3 + "cccccccccccccc \ + ddddddddddddddddddd" # comment 4 +) ```