Skip to content

Commit

Permalink
Handle more huggable immediately nested parens/brackets. (#4012)
Browse files Browse the repository at this point in the history
Fixes #4011
  • Loading branch information
yilei committed Nov 18, 2023
1 parent d93a942 commit 11da02d
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 89 deletions.
3 changes: 3 additions & 0 deletions CHANGES.md
Expand Up @@ -14,6 +14,9 @@

<!-- Changes that affect Black's preview style -->

- Additional cases of immediately nested tuples, lists, and dictionaries are now
indented less (#4012)

### Configuration

<!-- Changes to how Black can be configured -->
Expand Down
36 changes: 16 additions & 20 deletions docs/conf.py
Expand Up @@ -149,15 +149,13 @@ def make_pypi_svg(version: str) -> None:
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(
master_doc,
"black.tex",
"Documentation for Black",
"Łukasz Langa and contributors to Black",
"manual",
)
]
latex_documents = [(
master_doc,
"black.tex",
"Documentation for Black",
"Łukasz Langa and contributors to Black",
"manual",
)]


# -- Options for manual page output ------------------------------------------
Expand All @@ -172,17 +170,15 @@ def make_pypi_svg(version: str) -> None:
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(
master_doc,
"Black",
"Documentation for Black",
author,
"Black",
"The uncompromising Python code formatter",
"Miscellaneous",
)
]
texinfo_documents = [(
master_doc,
"Black",
"Documentation for Black",
author,
"Black",
"The uncompromising Python code formatter",
"Miscellaneous",
)]


# -- Options for Epub output -------------------------------------------------
Expand Down
17 changes: 15 additions & 2 deletions docs/the_black_code_style/future_style.md
Expand Up @@ -116,8 +116,7 @@ my_dict = {
### Improved multiline dictionary and list indentation for sole function parameter

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

```python
foo(
Expand All @@ -127,6 +126,14 @@ foo(
3,
]
)

nested_array = [
[
1,
2,
3,
]
]
```

will be changed to:
Expand All @@ -137,6 +144,12 @@ foo([
2,
3,
])

nested_array = [[
1,
2,
3,
]]
```

This also applies to list and dictionary unpacking:
Expand Down
6 changes: 3 additions & 3 deletions src/black/cache.py
Expand Up @@ -124,9 +124,9 @@ def filtered_cached(self, sources: Iterable[Path]) -> Tuple[Set[Path], Set[Path]

def write(self, sources: Iterable[Path]) -> None:
"""Update the cache file data and write a new cache file."""
self.file_data.update(**{
str(src.resolve()): Cache.get_file_data(src) for src in sources
})
self.file_data.update(
**{str(src.resolve()): Cache.get_file_data(src) for src in sources}
)
try:
CACHE_DIR.mkdir(parents=True, exist_ok=True)
with tempfile.NamedTemporaryFile(
Expand Down
54 changes: 24 additions & 30 deletions src/black/handle_ipynb_magics.py
Expand Up @@ -17,36 +17,30 @@
from black.output import out
from black.report import NothingChanged

TRANSFORMED_MAGICS = frozenset(
(
"get_ipython().run_cell_magic",
"get_ipython().system",
"get_ipython().getoutput",
"get_ipython().run_line_magic",
)
)
TOKENS_TO_IGNORE = frozenset(
(
"ENDMARKER",
"NL",
"NEWLINE",
"COMMENT",
"DEDENT",
"UNIMPORTANT_WS",
"ESCAPED_NL",
)
)
PYTHON_CELL_MAGICS = frozenset(
(
"capture",
"prun",
"pypy",
"python",
"python3",
"time",
"timeit",
)
)
TRANSFORMED_MAGICS = frozenset((
"get_ipython().run_cell_magic",
"get_ipython().system",
"get_ipython().getoutput",
"get_ipython().run_line_magic",
))
TOKENS_TO_IGNORE = frozenset((
"ENDMARKER",
"NL",
"NEWLINE",
"COMMENT",
"DEDENT",
"UNIMPORTANT_WS",
"ESCAPED_NL",
))
PYTHON_CELL_MAGICS = frozenset((
"capture",
"prun",
"pypy",
"python",
"python3",
"time",
"timeit",
))
TOKEN_HEX = secrets.token_hex


Expand Down
69 changes: 55 additions & 14 deletions src/black/linegen.py
Expand Up @@ -817,25 +817,66 @@ def _first_right_hand_split(
body_leaves.reverse()
head_leaves.reverse()

if Preview.hug_parens_with_braces_and_square_brackets in line.mode:
is_unpacking = 1 if body_leaves[0].type in [token.STAR, token.DOUBLESTAR] else 0
if (
tail_leaves[0].type == token.RPAR
and tail_leaves[0].value
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]
body: Optional[Line] = None
if (
Preview.hug_parens_with_braces_and_square_brackets in line.mode
and tail_leaves[0].value
and tail_leaves[0].opening_bracket is head_leaves[-1]
):
inner_body_leaves = list(body_leaves)
hugged_opening_leaves: List[Leaf] = []
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]
):
head_leaves = head_leaves + body_leaves[: 1 + is_unpacking]
tail_leaves = body_leaves[-1:] + tail_leaves
body_leaves = body_leaves[1 + is_unpacking : -1]
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_leaves, line, opening_bracket, component=_BracketSplitComponent.head
)
body = bracket_split_build_line(
body_leaves, line, opening_bracket, component=_BracketSplitComponent.body
)
if body is None:
body = bracket_split_build_line(
body_leaves, line, opening_bracket, component=_BracketSplitComponent.body
)
tail = bracket_split_build_line(
tail_leaves, line, opening_bracket, component=_BracketSplitComponent.tail
)
Expand Down

0 comments on commit 11da02d

Please sign in to comment.