Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix long case blocks not split into multiple lines #4024

Merged
merged 13 commits into from
Nov 7, 2023
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
indented less (#3964)
- Multiline list and dict unpacking as the sole argument to a function is now also
indented less (#3992)
- Fix a bug where long `case` blocks were not split into multiple lines. Also enable
general trailing comma rules on `case` blocks (#4024)
- Keep requiring two empty lines between module-level docstring and first function or
class definition. (#4028)

Expand Down
24 changes: 23 additions & 1 deletion src/black/linegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -1229,7 +1229,7 @@ def normalize_prefix(leaf: Leaf, *, inside_brackets: bool) -> None:
leaf.prefix = ""


def normalize_invisible_parens(
def normalize_invisible_parens( # noqa: C901
node: Node, parens_after: Set[str], *, mode: Mode, features: Collection[Feature]
) -> None:
"""Make existing optional parentheses invisible or create new ones.
Expand Down Expand Up @@ -1260,6 +1260,17 @@ def normalize_invisible_parens(
child, parens_after=parens_after, mode=mode, features=features
)

# Fixes a bug where invisible parens are not properly wrapped around
# case blocks.
if (
isinstance(child, Node)
and child.type == syms.case_block
and Preview.long_case_block_line_splitting in mode
):
normalize_invisible_parens(
child, parens_after={"case"}, mode=mode, features=features
)

# Add parentheses around long tuple unpacking in assignments.
if (
index == 0
Expand Down Expand Up @@ -1305,6 +1316,17 @@ def normalize_invisible_parens(
# invisible parentheses to work more precisely.
continue

elif (
isinstance(child, Leaf)
and child.next_sibling is not None
and child.next_sibling.type == token.COLON
and child.value == "case"
and Preview.long_case_block_line_splitting in mode
):
# A special patch for "case case:" scenario, the second occurrence
# of case will be not parsed as a Python keyword.
break

elif not (isinstance(child, Leaf) and is_multiline_string(child)):
wrap_in_parentheses(node, child, visible=False)

Expand Down
1 change: 1 addition & 0 deletions src/black/mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ class Preview(Enum):
hug_parens_with_braces_and_square_brackets = auto()
allow_empty_first_line_before_new_block_or_comment = auto()
single_line_format_skip_with_multiple_comments = auto()
long_case_block_line_splitting = auto()


class Deprecated(UserWarning):
Expand Down
22 changes: 2 additions & 20 deletions tests/data/cases/pattern_matching_extras.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,6 @@ def func(match: case, case: match) -> case:
...


match maybe, multiple:
case perhaps, 5:
pass
case perhaps, 6,:
pass


match more := (than, one), indeed,:
case _, (5, 6):
pass
case [[5], (6)], [7],:
pass
case _:
pass


match a, *b, c:
case [*_]:
assert "seq" == _
Expand All @@ -67,12 +51,12 @@ def func(match: case, case: match) -> case:
),
):
pass

case [a as match]:
pass

case case:
pass
case something:
pass


match match:
Expand All @@ -98,10 +82,8 @@ def func(match: case, case: match) -> case:
match something:
case 1 as a:
pass

case 2 as b, 3 as c:
pass

case 4 as d, (5 as e), (6 | 7 as g), *h:
pass

Expand Down
34 changes: 34 additions & 0 deletions tests/data/cases/preview_pattern_matching_long.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# flags: --preview --minimum-version=3.10
match x:
case "abcd" | "abcd" | "abcd" :
pass
case "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd":
pass
case xxxxxxxxxxxxxxxxxxxxxxx:
pass

# output

match x:
case "abcd" | "abcd" | "abcd":
pass
case (
"abcd"
| "abcd"
| "abcd"
| "abcd"
| "abcd"
| "abcd"
| "abcd"
| "abcd"
| "abcd"
| "abcd"
| "abcd"
| "abcd"
| "abcd"
| "abcd"
| "abcd"
):
pass
case xxxxxxxxxxxxxxxxxxxxxxx:
pass
39 changes: 39 additions & 0 deletions tests/data/cases/preview_pattern_matching_trailing_comma.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# flags: --preview --minimum-version=3.10
match maybe, multiple:
case perhaps, 5:
pass
case perhaps, 6,:
pass


match more := (than, one), indeed,:
case _, (5, 6):
pass
case [[5], (6)], [7],:
pass
case _:
pass


# output

match maybe, multiple:
case perhaps, 5:
pass
case (
perhaps,
6,
):
pass


match more := (than, one), indeed,:
case _, (5, 6):
pass
case (
[[5], (6)],
[7],
):
pass
case _:
pass