Skip to content

Commit

Permalink
Actually add trailing commas to collection literals even if there are…
Browse files Browse the repository at this point in the history
… terminating comments (#3393)


Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
Co-authored-by: Richard Si <sichard26@gmail.com>
  • Loading branch information
3 people committed Feb 5, 2023
1 parent ea5293b commit ff53fc1
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 21 deletions.
3 changes: 3 additions & 0 deletions CHANGES.md
Expand Up @@ -14,6 +14,9 @@

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

- Add trailing commas to collection literals even if there's a comment after the last
entry (#3393)

### Configuration

<!-- Changes to how Black can be configured -->
Expand Down
62 changes: 46 additions & 16 deletions src/black/linegen.py
Expand Up @@ -520,7 +520,7 @@ def transform_line(
else:

def _rhs(
self: object, line: Line, features: Collection[Feature]
self: object, line: Line, features: Collection[Feature], mode: Mode
) -> Iterator[Line]:
"""Wraps calls to `right_hand_split`.
Expand Down Expand Up @@ -604,7 +604,9 @@ class _BracketSplitComponent(Enum):
tail = auto()


def left_hand_split(line: Line, _features: Collection[Feature] = ()) -> Iterator[Line]:
def left_hand_split(
line: Line, _features: Collection[Feature], mode: Mode
) -> Iterator[Line]:
"""Split line into many lines, starting with the first matching bracket pair.
Note: this usually looks weird, only use this for function definitions.
Expand Down Expand Up @@ -940,16 +942,39 @@ def dont_increase_indentation(split_func: Transformer) -> Transformer:
"""

@wraps(split_func)
def split_wrapper(line: Line, features: Collection[Feature] = ()) -> Iterator[Line]:
for split_line in split_func(line, features):
def split_wrapper(
line: Line, features: Collection[Feature], mode: Mode
) -> Iterator[Line]:
for split_line in split_func(line, features, mode):
normalize_prefix(split_line.leaves[0], inside_brackets=True)
yield split_line

return split_wrapper


def _get_last_non_comment_leaf(line: Line) -> Optional[int]:
for leaf_idx in range(len(line.leaves) - 1, 0, -1):
if line.leaves[leaf_idx].type != STANDALONE_COMMENT:
return leaf_idx
return None


def _safe_add_trailing_comma(safe: bool, delimiter_priority: int, line: Line) -> Line:
if (
safe
and delimiter_priority == COMMA_PRIORITY
and line.leaves[-1].type != token.COMMA
and line.leaves[-1].type != STANDALONE_COMMENT
):
new_comma = Leaf(token.COMMA, ",")
line.append(new_comma)
return line


@dont_increase_indentation
def delimiter_split(line: Line, features: Collection[Feature] = ()) -> Iterator[Line]:
def delimiter_split(
line: Line, features: Collection[Feature], mode: Mode
) -> Iterator[Line]:
"""Split according to delimiters of the highest priority.
If the appropriate Features are given, the split will add trailing commas
Expand Down Expand Up @@ -989,7 +1014,8 @@ def append_to_line(leaf: Leaf) -> Iterator[Line]:
)
current_line.append(leaf)

for leaf in line.leaves:
last_non_comment_leaf = _get_last_non_comment_leaf(line)
for leaf_idx, leaf in enumerate(line.leaves):
yield from append_to_line(leaf)

for comment_after in line.comments_after(leaf):
Expand All @@ -1006,6 +1032,15 @@ def append_to_line(leaf: Leaf) -> Iterator[Line]:
trailing_comma_safe and Feature.TRAILING_COMMA_IN_CALL in features
)

if (
Preview.add_trailing_comma_consistently in mode
and last_leaf.type == STANDALONE_COMMENT
and leaf_idx == last_non_comment_leaf
):
current_line = _safe_add_trailing_comma(
trailing_comma_safe, delimiter_priority, current_line
)

leaf_priority = bt.delimiters.get(id(leaf))
if leaf_priority == delimiter_priority:
yield current_line
Expand All @@ -1014,20 +1049,15 @@ def append_to_line(leaf: Leaf) -> Iterator[Line]:
mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
)
if current_line:
if (
trailing_comma_safe
and delimiter_priority == COMMA_PRIORITY
and current_line.leaves[-1].type != token.COMMA
and current_line.leaves[-1].type != STANDALONE_COMMENT
):
new_comma = Leaf(token.COMMA, ",")
current_line.append(new_comma)
current_line = _safe_add_trailing_comma(
trailing_comma_safe, delimiter_priority, current_line
)
yield current_line


@dont_increase_indentation
def standalone_comment_split(
line: Line, features: Collection[Feature] = ()
line: Line, features: Collection[Feature], mode: Mode
) -> Iterator[Line]:
"""Split standalone comments from the rest of the line."""
if not line.contains_standalone_comments(0):
Expand Down Expand Up @@ -1480,7 +1510,7 @@ def run_transformer(
if not line_str:
line_str = line_to_string(line)
result: List[Line] = []
for transformed_line in transform(line, features):
for transformed_line in transform(line, features, mode):
if str(transformed_line).strip("\n") == line_str:
raise CannotTransform("Line transformer returned an unchanged result")

Expand Down
1 change: 1 addition & 0 deletions src/black/mode.py
Expand Up @@ -153,6 +153,7 @@ def supports_feature(target_versions: Set[TargetVersion], feature: Feature) -> b
class Preview(Enum):
"""Individual preview style features."""

add_trailing_comma_consistently = auto()
hex_codes_in_unicode_sequences = auto()
prefer_splitting_right_hand_side_of_assignments = auto()
# NOTE: string_processing requires wrap_long_dict_values_in_parens
Expand Down
12 changes: 8 additions & 4 deletions src/black/trans.py
Expand Up @@ -32,7 +32,7 @@

from black.comments import contains_pragma_comment
from black.lines import Line, append_leaves
from black.mode import Feature
from black.mode import Feature, Mode
from black.nodes import (
CLOSING_BRACKETS,
OPENING_BRACKETS,
Expand Down Expand Up @@ -63,7 +63,7 @@ class CannotTransform(Exception):
# types
T = TypeVar("T")
LN = Union[Leaf, Node]
Transformer = Callable[[Line, Collection[Feature]], Iterator[Line]]
Transformer = Callable[[Line, Collection[Feature], Mode], Iterator[Line]]
Index = int
NodeType = int
ParserState = int
Expand All @@ -81,7 +81,9 @@ def TErr(err_msg: str) -> Err[CannotTransform]:
return Err(cant_transform)


def hug_power_op(line: Line, features: Collection[Feature]) -> Iterator[Line]:
def hug_power_op(
line: Line, features: Collection[Feature], mode: Mode
) -> Iterator[Line]:
"""A transformer which normalizes spacing around power operators."""

# Performance optimization to avoid unnecessary Leaf clones and other ops.
Expand Down Expand Up @@ -228,7 +230,9 @@ def do_transform(
yield an CannotTransform after that point.)
"""

def __call__(self, line: Line, _features: Collection[Feature]) -> Iterator[Line]:
def __call__(
self, line: Line, _features: Collection[Feature], _mode: Mode
) -> Iterator[Line]:
"""
StringTransformer instances have a call signature that mirrors that of
the Transformer type.
Expand Down
55 changes: 55 additions & 0 deletions tests/data/preview/trailing_comma.py
@@ -0,0 +1,55 @@
e = {
"a": fun(msg, "ts"),
"longggggggggggggggid": ...,
"longgggggggggggggggggggkey": ..., "created": ...
# "longkey": ...
}
f = [
arg1,
arg2,
arg3, arg4
# comment
]
g = (
arg1,
arg2,
arg3, arg4
# comment
)
h = {
arg1,
arg2,
arg3, arg4
# comment
}

# output

e = {
"a": fun(msg, "ts"),
"longggggggggggggggid": ...,
"longgggggggggggggggggggkey": ...,
"created": ...,
# "longkey": ...
}
f = [
arg1,
arg2,
arg3,
arg4,
# comment
]
g = (
arg1,
arg2,
arg3,
arg4,
# comment
)
h = {
arg1,
arg2,
arg3,
arg4,
# comment
}
2 changes: 1 addition & 1 deletion tests/data/preview_context_managers/targeting_py39.py
Expand Up @@ -84,7 +84,7 @@
# First comment.
new_new_new1() as cm1,
# Second comment.
new_new_new2()
new_new_new2(),
# Last comment.
):
pass
Expand Down

0 comments on commit ff53fc1

Please sign in to comment.