Actually add trailing commas to collection literals even if there are terminating comments (#3393)

Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
Co-authored-by: Richard Si <sichard26@gmail.com>
This commit is contained in:
mainj12 2023-02-05 03:35:43 +00:00 committed by GitHub
parent ea5293b036
commit ff53fc1b97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 114 additions and 21 deletions

View File

@ -14,6 +14,9 @@
<!-- Changes that affect Black's preview style --> <!-- 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 ### Configuration
<!-- Changes to how Black can be configured --> <!-- Changes to how Black can be configured -->

View File

@ -520,7 +520,7 @@ def transform_line(
else: else:
def _rhs( def _rhs(
self: object, line: Line, features: Collection[Feature] self: object, line: Line, features: Collection[Feature], mode: Mode
) -> Iterator[Line]: ) -> Iterator[Line]:
"""Wraps calls to `right_hand_split`. """Wraps calls to `right_hand_split`.
@ -604,7 +604,9 @@ class _BracketSplitComponent(Enum):
tail = auto() 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. """Split line into many lines, starting with the first matching bracket pair.
Note: this usually looks weird, only use this for function definitions. Note: this usually looks weird, only use this for function definitions.
@ -940,16 +942,39 @@ def dont_increase_indentation(split_func: Transformer) -> Transformer:
""" """
@wraps(split_func) @wraps(split_func)
def split_wrapper(line: Line, features: Collection[Feature] = ()) -> Iterator[Line]: def split_wrapper(
for split_line in split_func(line, features): 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) normalize_prefix(split_line.leaves[0], inside_brackets=True)
yield split_line yield split_line
return split_wrapper 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 @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. """Split according to delimiters of the highest priority.
If the appropriate Features are given, the split will add trailing commas If the appropriate Features are given, the split will add trailing commas
@ -989,7 +1014,8 @@ def append_to_line(leaf: Leaf) -> Iterator[Line]:
) )
current_line.append(leaf) 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) yield from append_to_line(leaf)
for comment_after in line.comments_after(leaf): for comment_after in line.comments_after(leaf):
@ -1006,6 +1032,15 @@ def append_to_line(leaf: Leaf) -> Iterator[Line]:
trailing_comma_safe and Feature.TRAILING_COMMA_IN_CALL in features 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)) leaf_priority = bt.delimiters.get(id(leaf))
if leaf_priority == delimiter_priority: if leaf_priority == delimiter_priority:
yield current_line yield current_line
@ -1014,20 +1049,15 @@ def append_to_line(leaf: Leaf) -> Iterator[Line]:
mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
) )
if current_line: if current_line:
if ( current_line = _safe_add_trailing_comma(
trailing_comma_safe trailing_comma_safe, delimiter_priority, current_line
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)
yield current_line yield current_line
@dont_increase_indentation @dont_increase_indentation
def standalone_comment_split( def standalone_comment_split(
line: Line, features: Collection[Feature] = () line: Line, features: Collection[Feature], mode: Mode
) -> Iterator[Line]: ) -> Iterator[Line]:
"""Split standalone comments from the rest of the line.""" """Split standalone comments from the rest of the line."""
if not line.contains_standalone_comments(0): if not line.contains_standalone_comments(0):
@ -1480,7 +1510,7 @@ def run_transformer(
if not line_str: if not line_str:
line_str = line_to_string(line) line_str = line_to_string(line)
result: List[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: if str(transformed_line).strip("\n") == line_str:
raise CannotTransform("Line transformer returned an unchanged result") raise CannotTransform("Line transformer returned an unchanged result")

View File

@ -153,6 +153,7 @@ def supports_feature(target_versions: Set[TargetVersion], feature: Feature) -> b
class Preview(Enum): class Preview(Enum):
"""Individual preview style features.""" """Individual preview style features."""
add_trailing_comma_consistently = auto()
hex_codes_in_unicode_sequences = auto() hex_codes_in_unicode_sequences = auto()
prefer_splitting_right_hand_side_of_assignments = auto() prefer_splitting_right_hand_side_of_assignments = auto()
# NOTE: string_processing requires wrap_long_dict_values_in_parens # NOTE: string_processing requires wrap_long_dict_values_in_parens

View File

@ -32,7 +32,7 @@
from black.comments import contains_pragma_comment from black.comments import contains_pragma_comment
from black.lines import Line, append_leaves from black.lines import Line, append_leaves
from black.mode import Feature from black.mode import Feature, Mode
from black.nodes import ( from black.nodes import (
CLOSING_BRACKETS, CLOSING_BRACKETS,
OPENING_BRACKETS, OPENING_BRACKETS,
@ -63,7 +63,7 @@ class CannotTransform(Exception):
# types # types
T = TypeVar("T") T = TypeVar("T")
LN = Union[Leaf, Node] LN = Union[Leaf, Node]
Transformer = Callable[[Line, Collection[Feature]], Iterator[Line]] Transformer = Callable[[Line, Collection[Feature], Mode], Iterator[Line]]
Index = int Index = int
NodeType = int NodeType = int
ParserState = int ParserState = int
@ -81,7 +81,9 @@ def TErr(err_msg: str) -> Err[CannotTransform]:
return Err(cant_transform) 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.""" """A transformer which normalizes spacing around power operators."""
# Performance optimization to avoid unnecessary Leaf clones and other ops. # Performance optimization to avoid unnecessary Leaf clones and other ops.
@ -228,7 +230,9 @@ def do_transform(
yield an CannotTransform after that point.) 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 StringTransformer instances have a call signature that mirrors that of
the Transformer type. the Transformer type.

View File

@ -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
}

View File

@ -84,7 +84,7 @@
# First comment. # First comment.
new_new_new1() as cm1, new_new_new1() as cm1,
# Second comment. # Second comment.
new_new_new2() new_new_new2(),
# Last comment. # Last comment.
): ):
pass pass