Handle more huggable immediately nested parens/brackets. (#4012)

Fixes #4011
This commit is contained in:
Yilei Yang 2023-11-18 11:47:05 -08:00 committed by GitHub
parent d93a942a79
commit 11da02da72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 233 additions and 89 deletions

View File

@ -14,6 +14,9 @@
<!-- Changes that affect Black's preview style --> <!-- Changes that affect Black's preview style -->
- Additional cases of immediately nested tuples, lists, and dictionaries are now
indented less (#4012)
### Configuration ### Configuration
<!-- Changes to how Black can be configured --> <!-- Changes to how Black can be configured -->

View File

@ -149,15 +149,13 @@ def make_pypi_svg(version: str) -> None:
# Grouping the document tree into LaTeX files. List of tuples # Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, # (source start file, target name, title,
# author, documentclass [howto, manual, or own class]). # author, documentclass [howto, manual, or own class]).
latex_documents = [ latex_documents = [(
(
master_doc, master_doc,
"black.tex", "black.tex",
"Documentation for Black", "Documentation for Black",
"Łukasz Langa and contributors to Black", "Łukasz Langa and contributors to Black",
"manual", "manual",
) )]
]
# -- Options for manual page output ------------------------------------------ # -- Options for manual page output ------------------------------------------
@ -172,8 +170,7 @@ def make_pypi_svg(version: str) -> None:
# Grouping the document tree into Texinfo files. List of tuples # Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author, # (source start file, target name, title, author,
# dir menu entry, description, category) # dir menu entry, description, category)
texinfo_documents = [ texinfo_documents = [(
(
master_doc, master_doc,
"Black", "Black",
"Documentation for Black", "Documentation for Black",
@ -181,8 +178,7 @@ def make_pypi_svg(version: str) -> None:
"Black", "Black",
"The uncompromising Python code formatter", "The uncompromising Python code formatter",
"Miscellaneous", "Miscellaneous",
) )]
]
# -- Options for Epub output ------------------------------------------------- # -- Options for Epub output -------------------------------------------------

View File

@ -116,8 +116,7 @@ my_dict = {
### Improved multiline dictionary and list indentation for sole function parameter ### Improved multiline dictionary and list indentation for sole function parameter
For better readability and less verticality, _Black_ now pairs parentheses ("(", ")") For better readability and less verticality, _Black_ now pairs parentheses ("(", ")")
with braces ("{", "}") and square brackets ("[", "]") on the same line for single with braces ("{", "}") and square brackets ("[", "]") on the same line. For example:
parameter function calls. For example:
```python ```python
foo( foo(
@ -127,6 +126,14 @@ foo(
3, 3,
] ]
) )
nested_array = [
[
1,
2,
3,
]
]
``` ```
will be changed to: will be changed to:
@ -137,6 +144,12 @@ foo([
2, 2,
3, 3,
]) ])
nested_array = [[
1,
2,
3,
]]
``` ```
This also applies to list and dictionary unpacking: This also applies to list and dictionary unpacking:

View File

@ -124,9 +124,9 @@ def filtered_cached(self, sources: Iterable[Path]) -> Tuple[Set[Path], Set[Path]
def write(self, sources: Iterable[Path]) -> None: def write(self, sources: Iterable[Path]) -> None:
"""Update the cache file data and write a new cache file.""" """Update the cache file data and write a new cache file."""
self.file_data.update(**{ self.file_data.update(
str(src.resolve()): Cache.get_file_data(src) for src in sources **{str(src.resolve()): Cache.get_file_data(src) for src in sources}
}) )
try: try:
CACHE_DIR.mkdir(parents=True, exist_ok=True) CACHE_DIR.mkdir(parents=True, exist_ok=True)
with tempfile.NamedTemporaryFile( with tempfile.NamedTemporaryFile(

View File

@ -17,16 +17,13 @@
from black.output import out from black.output import out
from black.report import NothingChanged from black.report import NothingChanged
TRANSFORMED_MAGICS = frozenset( TRANSFORMED_MAGICS = frozenset((
(
"get_ipython().run_cell_magic", "get_ipython().run_cell_magic",
"get_ipython().system", "get_ipython().system",
"get_ipython().getoutput", "get_ipython().getoutput",
"get_ipython().run_line_magic", "get_ipython().run_line_magic",
) ))
) TOKENS_TO_IGNORE = frozenset((
TOKENS_TO_IGNORE = frozenset(
(
"ENDMARKER", "ENDMARKER",
"NL", "NL",
"NEWLINE", "NEWLINE",
@ -34,10 +31,8 @@
"DEDENT", "DEDENT",
"UNIMPORTANT_WS", "UNIMPORTANT_WS",
"ESCAPED_NL", "ESCAPED_NL",
) ))
) PYTHON_CELL_MAGICS = frozenset((
PYTHON_CELL_MAGICS = frozenset(
(
"capture", "capture",
"prun", "prun",
"pypy", "pypy",
@ -45,8 +40,7 @@
"python3", "python3",
"time", "time",
"timeit", "timeit",
) ))
)
TOKEN_HEX = secrets.token_hex TOKEN_HEX = secrets.token_hex

View File

@ -817,22 +817,63 @@ def _first_right_hand_split(
body_leaves.reverse() body_leaves.reverse()
head_leaves.reverse() head_leaves.reverse()
if Preview.hug_parens_with_braces_and_square_brackets in line.mode: body: Optional[Line] = None
is_unpacking = 1 if body_leaves[0].type in [token.STAR, token.DOUBLESTAR] else 0
if ( if (
tail_leaves[0].type == token.RPAR Preview.hug_parens_with_braces_and_square_brackets in line.mode
and tail_leaves[0].value and tail_leaves[0].value
and tail_leaves[0].opening_bracket is head_leaves[-1] and tail_leaves[0].opening_bracket is head_leaves[-1]
and body_leaves[-1].type in [token.RBRACE, token.RSQB]
and body_leaves[-1].opening_bracket is body_leaves[is_unpacking]
): ):
head_leaves = head_leaves + body_leaves[: 1 + is_unpacking] inner_body_leaves = list(body_leaves)
tail_leaves = body_leaves[-1:] + tail_leaves hugged_opening_leaves: List[Leaf] = []
body_leaves = body_leaves[1 + is_unpacking : -1] hugged_closing_leaves: List[Leaf] = []
is_unpacking = body_leaves[0].type in [token.STAR, token.DOUBLESTAR]
unpacking_offset: int = 1 if is_unpacking else 0
while (
len(inner_body_leaves) >= 2 + unpacking_offset
and inner_body_leaves[-1].type in CLOSING_BRACKETS
and inner_body_leaves[-1].opening_bracket
is inner_body_leaves[unpacking_offset]
):
if unpacking_offset:
hugged_opening_leaves.append(inner_body_leaves.pop(0))
unpacking_offset = 0
hugged_opening_leaves.append(inner_body_leaves.pop(0))
hugged_closing_leaves.insert(0, inner_body_leaves.pop())
if hugged_opening_leaves and inner_body_leaves:
inner_body = bracket_split_build_line(
inner_body_leaves,
line,
hugged_opening_leaves[-1],
component=_BracketSplitComponent.body,
)
if (
line.mode.magic_trailing_comma
and inner_body_leaves[-1].type == token.COMMA
):
should_hug = True
else:
line_length = line.mode.line_length - sum(
len(str(leaf))
for leaf in hugged_opening_leaves + hugged_closing_leaves
)
if is_line_short_enough(
inner_body, mode=replace(line.mode, line_length=line_length)
):
# Do not hug if it fits on a single line.
should_hug = False
else:
should_hug = True
if should_hug:
body_leaves = inner_body_leaves
head_leaves.extend(hugged_opening_leaves)
tail_leaves = hugged_closing_leaves + tail_leaves
body = inner_body # No need to re-calculate the body again later.
head = bracket_split_build_line( head = bracket_split_build_line(
head_leaves, line, opening_bracket, component=_BracketSplitComponent.head head_leaves, line, opening_bracket, component=_BracketSplitComponent.head
) )
if body is None:
body = bracket_split_build_line( body = bracket_split_build_line(
body_leaves, line, opening_bracket, component=_BracketSplitComponent.body body_leaves, line, opening_bracket, component=_BracketSplitComponent.body
) )

View File

@ -128,6 +128,19 @@ def foo_square_brackets(request):
func({"short 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"})
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"}})
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")))
func([["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]])
# Do not hug if the argument fits on a single line.
func({"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"})
func(("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"))
func(["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"])
func(**{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"})
func(*("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----"))
array = [{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}]
array = [("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")]
array = [["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]]
foooooooooooooooooooo( foooooooooooooooooooo(
[{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size} [{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size}
@ -137,6 +150,13 @@ def foo_square_brackets(request):
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], {x}, "a string", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], {x}, "a string", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
) )
nested_mapping = {"key": [{"a very long key 1": "with a very long value", "a very long key 2": "with a very long value"}]}
nested_array = [[["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]]]
explicit_exploding = [[["short", "line",],],]
single_item_do_not_explode = Context({
"version": get_docs_version(),
})
foo(*["long long long long long line", "long long long long long line", "long long long long long line"]) foo(*["long long long long long line", "long long long long long line", "long long long long long line"])
foo(*[str(i) for i in range(100000000000000000000000000000000000000000000000000000000000)]) foo(*[str(i) for i in range(100000000000000000000000000000000000000000000000000000000000)])
@ -152,6 +172,9 @@ def foo_square_brackets(request):
foo(**{x: y for x, y in enumerate(["long long long long line","long long long long line"])}) foo(**{x: y for x, y in enumerate(["long long long long line","long long long long line"])})
# Edge case when deciding whether to hug the brackets without inner content.
very_very_very_long_variable = very_very_very_long_module.VeryVeryVeryVeryLongClassName([[]])
for foo in ["a", "b"]: for foo in ["a", "b"]:
output.extend([ output.extend([
individual individual
@ -276,9 +299,9 @@ def foo_square_brackets(request):
) )
func([x for x in "short line"]) func([x for x in "short line"])
func([ func(
x for x in "long line long line long line long line long line long line long line" [x for x in "long line long line long line long line long line long line long line"]
]) )
func([ func([
x x
for x in [ for x in [
@ -295,15 +318,60 @@ def foo_square_brackets(request):
"long long long long line", "long long long long line",
"long long long long long line", "long long long long long line",
}) })
func({ func({{
{
"long line", "long line",
"long long line", "long long line",
"long long long line", "long long long line",
"long long long long line", "long long long long line",
"long 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",
))
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",
]])
# Do not hug if the argument fits on a single line.
func(
{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}
)
func(
("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")
)
func(
["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]
)
func(
**{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"}
)
func(
*("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----")
)
array = [
{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}
]
array = [
("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")
]
array = [
["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]
]
foooooooooooooooooooo( foooooooooooooooooooo(
[{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size} [{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size}
@ -313,6 +381,31 @@ def foo_square_brackets(request):
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], {x}, "a string", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], {x}, "a string", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
) )
nested_mapping = {
"key": [{
"a very long key 1": "with a very long value",
"a very long key 2": "with a very long value",
}]
}
nested_array = [[[
"long line",
"long long line",
"long long long line",
"long long long long line",
"long long long long long line",
]]]
explicit_exploding = [
[
[
"short",
"line",
],
],
]
single_item_do_not_explode = Context({
"version": get_docs_version(),
})
foo(*[ foo(*[
"long long long long long line", "long long long long long line",
"long long long long long line", "long long long long long line",
@ -334,6 +427,11 @@ def foo_square_brackets(request):
x: y for x, y in enumerate(["long long long long line", "long long long long line"]) x: y for x, y in enumerate(["long long long long line", "long long long long line"])
}) })
# Edge case when deciding whether to hug the brackets without inner content.
very_very_very_long_variable = very_very_very_long_module.VeryVeryVeryVeryLongClassName(
[[]]
)
for foo in ["a", "b"]: for foo in ["a", "b"]:
output.extend([ output.extend([
individual individual

View File

@ -611,14 +611,13 @@ def foo():
class A: class A:
def foo(): def foo():
XXXXXXXXXXXX.append( XXXXXXXXXXXX.append((
( "xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx, xxxx_xxxx_xxxxxxxxxx={})".format(
"xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx, xxxx_xxxx_xxxxxxxxxx={})" xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx
.format(xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx), ),
my_var, my_var,
my_other_var, my_other_var,
) ))
)
class A: class A: