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:
parent
9edba85f71
commit
882d8795c6
@ -12,7 +12,7 @@
|
||||
|
||||
### Preview style
|
||||
|
||||
<!-- Changes that affect Black's preview style -->
|
||||
- Fix merging implicit multiline strings that have inline comments (#3956)
|
||||
|
||||
### Configuration
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
```
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user