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

[925] Improve multiline dictionary and list indentation for sole function parameter #3964

Merged
merged 14 commits into from
Oct 25, 2023
5 changes: 4 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@

### Preview style

<!-- Changes that affect Black's preview style -->
- Fix merging implicit multiline strings that have inline comments (#3956)
- Multiline dictionaries and lists that are the sole argument to a function are now
indented less (#3964)
- Allow empty first line after block open before a comment or compound statement (#3967)
henriholopainen marked this conversation as resolved.
Show resolved Hide resolved

### Configuration

Expand Down
26 changes: 26 additions & 0 deletions docs/the_black_code_style/future_style.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,32 @@ my_dict = {
}
```

### Improved multiline dictionary and list indentation for sole function parameter
JelleZijlstra marked this conversation as resolved.
Show resolved Hide resolved

For better readability and less verticality, _Black_ now pairs parantheses ("(", ")")
with braces ("{", "}") and square brackets ("[", "]") on the same line for single
parameter function calls. For example:

```python
foo(
[
1,
2,
3,
]
)
```

will be changed to:

```python
foo([
1,
2,
3,
])
```

### Improved multiline string handling

_Black_ is smarter when formatting multiline strings, especially in function arguments,
Expand Down
13 changes: 13 additions & 0 deletions src/black/linegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,19 @@ def _first_right_hand_split(
tail_leaves.reverse()
body_leaves.reverse()
head_leaves.reverse()

if Preview.hug_parens_with_braces_and_square_brackets in line.mode:
if (
tail_leaves[0].type == token.RPAR
and tail_leaves[0].value
and tail_leaves[0].opening_bracket is head_leaves[-1]
henriholopainen marked this conversation as resolved.
Show resolved Hide resolved
and body_leaves[-1].type in [token.RBRACE, token.RSQB]
and body_leaves[-1].opening_bracket is body_leaves[0]
henriholopainen marked this conversation as resolved.
Show resolved Hide resolved
):
head_leaves = head_leaves + body_leaves[:1]
tail_leaves = body_leaves[-1:] + tail_leaves
body_leaves = body_leaves[1:-1]

head = bracket_split_build_line(
head_leaves, line, opening_bracket, component=_BracketSplitComponent.head
)
Expand Down
1 change: 1 addition & 0 deletions src/black/mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ class Preview(Enum):
module_docstring_newlines = auto()
accept_raw_docstrings = auto()
fix_power_op_line_length = auto()
hug_parens_with_braces_and_square_brackets = auto()
allow_empty_first_line_before_new_block_or_comment = auto()


Expand Down
273 changes: 273 additions & 0 deletions tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
# flags: --preview
def foo_brackets(request):
return JsonResponse(
JelleZijlstra marked this conversation as resolved.
Show resolved Hide resolved
{
"var_1": foo,
"var_2": bar,
}
)

def foo_square_brackets(request):
return JsonResponse(
[
"var_1",
"var_2",
]
)

func({"a": 37, "b": 42, "c": 927, "aaaaaaaaaaaaaaaaaaaaaaaaa": 11111111111111111111111111111111111111111})

func(["random_string_number_one","random_string_number_two","random_string_number_three","random_string_number_four"])

func(
{
# expand me
'a':37,
'b':42,
'c':927
}
)

func(
[
'a',
'b',
'c',
]
)

func( # a
[ # b
"c", # c
"d", # d
"e", # e
] # f
) # g

func( # a
{ # b
"c": 1, # c
"d": 2, # d
"e": 3, # e
} # f
) # g

func(
# preserve me
[
"c",
"d",
"e",
]
)

func(
[ # preserve me but hug brackets
"c",
"d",
"e",
]
)

func(
[
# preserve me but hug brackets
"c",
"d",
"e",
]
)

func(
[
"c",
# preserve me but hug brackets
"d",
"e",
]
)

func(
[
"c",
"d",
"e",
# preserve me but hug brackets
]
)

func(
[
"c",
"d",
"e",
] # preserve me but hug brackets
)

func(
[
"c",
"d",
"e",
]
# preserve me
)

func([x for x in "short line"])
func([x for x in "long line long line long line long line long line long line long line"])
func([x for x in [x for x in "long line long line long line long line long line long line long line"]])

func({"short line"})
func({"long line", "long long line", "long long long line", "long long long long line", "long long long long long line"})
func({{"long line", "long long line", "long long long line", "long long long long line", "long long long long long line"}})

foooooooooooooooooooo(
[{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size}
)

baaaaaaaaaaaaar(
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], {x}, "a string", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
)

# output
def foo_brackets(request):
return JsonResponse({
"var_1": foo,
"var_2": bar,
})


def foo_square_brackets(request):
return JsonResponse([
"var_1",
"var_2",
])


func({
"a": 37,
"b": 42,
"c": 927,
"aaaaaaaaaaaaaaaaaaaaaaaaa": 11111111111111111111111111111111111111111,
})

func([
"random_string_number_one",
"random_string_number_two",
"random_string_number_three",
"random_string_number_four",
])

func({
# expand me
"a": 37,
"b": 42,
"c": 927,
})

func([
"a",
"b",
"c",
])

func([ # a # b
"c", # c
"d", # d
"e", # e
]) # f # g

func({ # a # b
"c": 1, # c
"d": 2, # d
"e": 3, # e
}) # f # g

func(
# preserve me
[
"c",
"d",
"e",
]
)

func([ # preserve me but hug brackets
"c",
"d",
"e",
])

func([
# preserve me but hug brackets
"c",
"d",
"e",
])

func([
"c",
# preserve me but hug brackets
"d",
"e",
])

func([
"c",
"d",
"e",
# preserve me but hug brackets
])

func([
"c",
"d",
"e",
]) # preserve me but hug brackets

func(
[
"c",
"d",
"e",
]
# preserve me
)

func([x for x in "short line"])
func([
x for x in "long line long line long line long line long line long line long line"
])
func([
x
for x in [
x
for x in "long line long line long line long line long line long line long line"
]
])

func({"short line"})
func({
"long line",
"long long line",
"long long long line",
"long long long long line",
"long long long long long line",
})
func({
{
"long line",
"long long line",
"long long long line",
"long long long long line",
"long long long long long line",
}
})

foooooooooooooooooooo(
[{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size}
)

baaaaaaaaaaaaar(
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], {x}, "a string", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
)
22 changes: 10 additions & 12 deletions tests/data/cases/preview_long_strings__regression.py
Original file line number Diff line number Diff line change
Expand Up @@ -962,19 +962,17 @@ def who(self):
)


xxxxxxx_xxxxxx_xxxxxxx = xxx(
[
xxxxxxxxxxxx(
xxxxxx_xxxxxxx=(
'((x.aaaaaaaaa = "xxxxxx.xxxxxxxxxxxxxxxxxxxxx") || (x.xxxxxxxxx ='
' "xxxxxxxxxxxx")) && '
# xxxxx xxxxxxxxxxxx xxxx xxx (xxxxxxxxxxxxxxxx) xx x xxxxxxxxx xx xxxxxx.
"(x.bbbbbbbbbbbb.xxx != "
'"xxx:xxx:xxx::cccccccccccc:xxxxxxx-xxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxx") && '
)
xxxxxxx_xxxxxx_xxxxxxx = xxx([
xxxxxxxxxxxx(
xxxxxx_xxxxxxx=(
'((x.aaaaaaaaa = "xxxxxx.xxxxxxxxxxxxxxxxxxxxx") || (x.xxxxxxxxx ='
' "xxxxxxxxxxxx")) && '
# xxxxx xxxxxxxxxxxx xxxx xxx (xxxxxxxxxxxxxxxx) xx x xxxxxxxxx xx xxxxxx.
"(x.bbbbbbbbbbbb.xxx != "
'"xxx:xxx:xxx::cccccccccccc:xxxxxxx-xxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxx") && '
)
]
)
)
])

if __name__ == "__main__":
for i in range(4, 8):
Expand Down
Loading