Fix merging implicit multiline strings that have inline comments (#3956)

* Fix test behaviour

* Add new test cases

* Skip merging strings that have inline comments

* Don't merge lines with multiline strings with inline comments

* Changelog entry

* Document implicit multiline string merging rules

* Fix PR number
This commit is contained in:
Henri Holopainen 2023-10-20 06:09:33 +03:00 committed by GitHub
parent 9edba85f71
commit 882d8795c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 125 additions and 5 deletions

View File

@ -12,7 +12,7 @@
### Preview style
<!-- Changes that affect Black's preview style -->
- Fix merging implicit multiline strings that have inline comments (#3956)
### Configuration

View File

@ -160,3 +160,67 @@ MULTILINE = """
foobar
""".replace("\n", "")
```
Implicit multiline strings are special, because they can have inline comments. Strings
without comments are merged, for example
```python
s = (
"An "
"implicit "
"multiline "
"string"
)
```
becomes
```python
s = "An implicit multiline string"
```
A comment on any line of the string (or between two string lines) will block the
merging, so
```python
s = (
"An " # Important comment concerning just this line
"implicit "
"multiline "
"string"
)
```
and
```python
s = (
"An "
"implicit "
# Comment in between
"multiline "
"string"
)
```
will not be merged. Having the comment after or before the string lines (but still
inside the parens) will merge the string. For example
```python
s = ( # Top comment
"An "
"implicit "
"multiline "
"string"
# Bottom comment
)
```
becomes
```python
s = ( # Top comment
"An implicit multiline string"
# Bottom comment
)
```

View File

@ -587,6 +587,7 @@ def transform_line(
or line.contains_unsplittable_type_ignore()
)
and not (line.inside_brackets and line.contains_standalone_comments())
and not line.contains_implicit_multiline_string_with_comments()
):
# Only apply basic string preprocessing, since lines shouldn't be split here.
if Preview.string_processing in mode:

View File

@ -239,6 +239,21 @@ def contains_standalone_comments(self, depth_limit: int = sys.maxsize) -> bool:
return False
def contains_implicit_multiline_string_with_comments(self) -> bool:
"""Chck if we have an implicit multiline string with comments on the line"""
for leaf_type, leaf_group_iterator in itertools.groupby(
self.leaves, lambda leaf: leaf.type
):
if leaf_type != token.STRING:
continue
leaf_list = list(leaf_group_iterator)
if len(leaf_list) == 1:
continue
for leaf in leaf_list:
if self.comments_after(leaf):
return True
return False
def contains_uncollapsable_type_comments(self) -> bool:
ignored_ids = set()
try:

View File

@ -390,7 +390,19 @@ def do_match(self, line: Line) -> TMatchResult:
and is_valid_index(idx + 1)
and LL[idx + 1].type == token.STRING
):
if not is_part_of_annotation(leaf):
# Let's check if the string group contains an inline comment
# If we have a comment inline, we don't merge the strings
contains_comment = False
i = idx
while is_valid_index(i):
if LL[i].type != token.STRING:
break
if line.comments_after(LL[i]):
contains_comment = True
break
i += 1
if not is_part_of_annotation(leaf) and not contains_comment:
string_indices.append(idx)
# Advance to the next non-STRING leaf.

View File

@ -210,8 +210,8 @@ def foo():
some_tuple = ("some string", "some string" " which should be joined")
some_commented_string = (
"This string is long but not so long that it needs hahahah toooooo be so greatttt" # This comment gets thrown to the top.
some_commented_string = ( # This comment stays at the top.
"This string is long but not so long that it needs hahahah toooooo be so greatttt"
" {} that I just can't think of any more good words to say about it at"
" allllllllllll".format("ha") # comments here are fine
)
@ -834,7 +834,7 @@ def foo():
some_tuple = ("some string", "some string which should be joined")
some_commented_string = ( # This comment gets thrown to the top.
some_commented_string = ( # This comment stays at the top.
"This string is long but not so long that it needs hahahah toooooo be so greatttt"
" {} that I just can't think of any more good words to say about it at"
" allllllllllll".format("ha") # comments here are fine

View File

@ -157,6 +157,24 @@ def dastardly_default_value(
`--global-option` is reserved to flags like `--verbose` or `--quiet`.
"""
this_will_become_one_line = (
"a"
"b"
"c"
)
this_will_stay_on_three_lines = (
"a" # comment
"b"
"c"
)
this_will_also_become_one_line = ( # comment
"a"
"b"
"c"
)
# output
"""cow
say""",
@ -357,3 +375,13 @@ def dastardly_default_value(
Please use `--build-option` instead,
`--global-option` is reserved to flags like `--verbose` or `--quiet`.
"""
this_will_become_one_line = "abc"
this_will_stay_on_three_lines = (
"a" # comment
"b"
"c"
)
this_will_also_become_one_line = "abc" # comment