Repository: psf/black

Issue:
String processing produces code exceeding length limit
**Describe the bug**

String processing produces code exceeding the length limit when it was already formatted properly before.

**To Reproduce**

For example:
1. Take this file:
```py
class X:
    async def foo(self):
        msg = ""
        for candidate in CANDIDATES:
            msg += (
                "**{candidate.object_type} {candidate.rev}**"
                " - {candidate.description}\n"
            )
```
2. Run _Black_ on it with these arguments `--experimental-string-processing`
3. See the reformatted code that exceeds line length limit:
```py
class X:
    async def foo(self):
        msg = ""
        for candidate in CANDIDATES:
            msg += "**{candidate.object_type} {candidate.rev}** - {candidate.description}\n"
```

**Expected behavior**

I expected the code to remain unchanged.

**Environment (please complete the following information):**

- Version: main (`cf75673` at the time of testing)
- OS and Python version: Windows 10/Python 3.8.9

**Does this bug also happen on main?**

Yes.

**Additional context**

I was unable to find another issue that would seem to be the same as this though it's possible I missed something. Just to be sure it's not about the same thing #2284 is, I also tried out the changes from #2292 (since they apparently fix some edge cases that weren't fixed by #2286.
Also, the original code actually contained f-string but to rule out that it has anything to do with #2293, I changed it to use regular strings which as you can see from the issue description didn't change the end result.



Discussion:
Probably what's also important here is that Black doesn't try to split this string despite it being too long:
```py
class X:
    async def foo(self):
        msg = ""
        for candidate in CANDIDATES:
            msg += "**{candidate.object_type} {candidate.rev}** - {candidate.description}\n"
```

After a second look, it appears that usage of `+=` is the key issue here - it works fine when replacing it with `=`
Does #2292 fix this?
Never mind, you already discussed it.

Resolving patch:
diff --git a/src/black/trans.py b/src/black/trans.py
--- a/src/black/trans.py
+++ b/src/black/trans.py
@@ -867,7 +867,7 @@ def _get_max_string_length(self, line: Line, string_idx: int) -> int:
                 # WMA4 a space, a comma, and a closing bracket [e.g. `), STRING`].
                 offset += 3
 
-            if P.type in [token.COLON, token.EQUAL, token.NAME]:
+            if P.type in [token.COLON, token.EQUAL, token.PLUSEQUAL, token.NAME]:
                 # This conditional branch is meant to handle dictionary keys,
                 # variable assignments, 'return STRING' statement lines, and
                 # 'else STRING' ternary expression lines.


Test patch:
diff --git a/tests/data/long_strings__edge_case.py b/tests/data/long_strings__edge_case.py
--- a/tests/data/long_strings__edge_case.py
+++ b/tests/data/long_strings__edge_case.py
@@ -32,6 +32,9 @@
 assert str(result) == "This long string should be split at some point right close to or around hereeeeeee"
 assert str(result) < "This long string should be split at some point right close to or around hereeeeee"
 assert "A format string: %s" % "This long string should be split at some point right close to or around hereeeeeee" != result
+msg += "This long string should be wrapped in parens at some point right around hereeeee"
+msg += "This long string should be split at some point right close to or around hereeeeeeee"
+msg += "This long string should not be split at any point ever since it is just righttt"
 
 
 # output
@@ -127,3 +130,11 @@
     " hereeeeeee"
     != result
 )
+msg += (
+    "This long string should be wrapped in parens at some point right around hereeeee"
+)
+msg += (
+    "This long string should be split at some point right close to or around"
+    " hereeeeeeee"
+)
+msg += "This long string should not be split at any point ever since it is just righttt"
diff --git a/tests/data/long_strings__regression.py b/tests/data/long_strings__regression.py
--- a/tests/data/long_strings__regression.py
+++ b/tests/data/long_strings__regression.py
@@ -407,6 +407,16 @@ def _legacy_listen_examples():
     )
 
 
+class X:
+    async def foo(self):
+        msg = ""
+        for candidate in CANDIDATES:
+            msg += (
+                "**{candidate.object_type} {candidate.rev}**"
+                " - {candidate.description}\n"
+            )
+
+
 temp_msg = (
     f"{f'{humanize_number(pos)}.': <{pound_len+2}} "
     f"{balance: <{bal_len + 5}} "
@@ -948,6 +958,16 @@ def _legacy_listen_examples():
     )
 
 
+class X:
+    async def foo(self):
+        msg = ""
+        for candidate in CANDIDATES:
+            msg += (
+                "**{candidate.object_type} {candidate.rev}**"
+                " - {candidate.description}\n"
+            )
+
+
 temp_msg = (
     f"{f'{humanize_number(pos)}.': <{pound_len+2}} "
     f"{balance: <{bal_len + 5}} "