From e911c79809c4fd9b0773dea5b6a0e710b59614cf Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C5=81ukasz=20Langa?= Date: Tue, 20 Mar 2018 18:15:20 -0700 Subject: [PATCH 1/1] Don't remove single empty lines outside of bracketed expressions Fixes #19 --- README.md | 3 + black.py | 30 +++++--- tests/comments2.py | 1 + tests/empty_lines.py | 173 +++++++++++++++++++++++++++++++++++++++++++ tests/test_black.py | 8 ++ 5 files changed, 203 insertions(+), 12 deletions(-) create mode 100644 tests/empty_lines.py diff --git a/README.md b/README.md index 1b83308..e4ccef2 100644 --- a/README.md +++ b/README.md @@ -259,6 +259,9 @@ More details can be found in [CONTRIBUTING](CONTRIBUTING.md). ### 18.3a3 +* don't remove single empty lines outside of bracketed expressions + (#19) + * added ability to pipe formatting from stdin to stdin (#25) * restored ability to format code with legacy usage of `async` as diff --git a/black.py b/black.py index 74329d2..8a4eb05 100644 --- a/black.py +++ b/black.py @@ -745,8 +745,9 @@ class LineGenerator(Visitor[Line]): def visit_default(self, node: LN) -> Iterator[Line]: if isinstance(node, Leaf): + any_open_brackets = self.current_line.bracket_tracker.any_open_brackets() for comment in generate_comments(node): - if self.current_line.bracket_tracker.any_open_brackets(): + if any_open_brackets: # any comment within brackets is subject to splitting self.current_line.append(comment) elif comment.type == token.COMMENT: @@ -758,7 +759,7 @@ class LineGenerator(Visitor[Line]): # regular standalone comment, to be processed later (see # docstring in `generate_comments()` self.standalone_comments.append(comment) - normalize_prefix(node) + normalize_prefix(node, inside_brackets=any_open_brackets) if node.type not in WHITESPACE: for comment in self.standalone_comments: yield from self.line() @@ -1238,7 +1239,7 @@ def left_hand_split(line: Line, py36: bool = False) -> Iterator[Line]: current_leaves = body_leaves # Since body is a new indent level, remove spurious leading whitespace. if body_leaves: - normalize_prefix(body_leaves[0]) + normalize_prefix(body_leaves[0], inside_brackets=True) # Build the new lines. for result, leaves in ( (head, head_leaves), (body, body_leaves), (tail, tail_leaves) @@ -1278,7 +1279,7 @@ def right_hand_split(line: Line, py36: bool = False) -> Iterator[Line]: head_leaves.reverse() # Since body is a new indent level, remove spurious leading whitespace. if body_leaves: - normalize_prefix(body_leaves[0]) + normalize_prefix(body_leaves[0], inside_brackets=True) # Build the new lines. for result, leaves in ( (head, head_leaves), (body, body_leaves), (tail, tail_leaves) @@ -1342,7 +1343,7 @@ def delimiter_split(line: Line, py36: bool = False) -> Iterator[Line]: trailing_comma_safe = trailing_comma_safe and py36 leaf_priority = delimiters.get(id(leaf)) if leaf_priority == delimiter_priority: - normalize_prefix(current_line.leaves[0]) + normalize_prefix(current_line.leaves[0], inside_brackets=True) yield current_line current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets) @@ -1353,7 +1354,7 @@ def delimiter_split(line: Line, py36: bool = False) -> Iterator[Line]: and trailing_comma_safe ): current_line.append(Leaf(token.COMMA, ',')) - normalize_prefix(current_line.leaves[0]) + normalize_prefix(current_line.leaves[0], inside_brackets=True) yield current_line @@ -1371,13 +1372,18 @@ def is_import(leaf: Leaf) -> bool: ) -def normalize_prefix(leaf: Leaf) -> None: - """Leave existing extra newlines for imports. Remove everything else.""" - if is_import(leaf): +def normalize_prefix(leaf: Leaf, *, inside_brackets: bool) -> None: + """Leave existing extra newlines if not `inside_brackets`. + + Remove everything else. Note: don't use backslashes for formatting or + you'll lose your voting rights. + """ + if not inside_brackets: spl = leaf.prefix.split('#', 1) - nl_count = spl[0].count('\n') - leaf.prefix = '\n' * nl_count - return + if '\\' not in spl[0]: + nl_count = spl[0].count('\n') + leaf.prefix = '\n' * nl_count + return leaf.prefix = '' diff --git a/tests/comments2.py b/tests/comments2.py index 6689c88..da4f7a1 100644 --- a/tests/comments2.py +++ b/tests/comments2.py @@ -159,6 +159,7 @@ short """, arg3=True, ) + ############################################################################ call2( # short diff --git a/tests/empty_lines.py b/tests/empty_lines.py new file mode 100644 index 0000000..fe86b38 --- /dev/null +++ b/tests/empty_lines.py @@ -0,0 +1,173 @@ +def f(): + NO = '' + SPACE = ' ' + DOUBLESPACE = ' ' + + t = leaf.type + p = leaf.parent # trailing comment + v = leaf.value + + if t in ALWAYS_NO_SPACE: + pass + if t == token.COMMENT: # another trailing comment + return DOUBLESPACE + + assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}" + + prev = leaf.prev_sibling + if not prev: + prevp = preceding_leaf(p) + if not prevp or prevp.type in OPENING_BRACKETS: + + return NO + + if prevp.type == token.EQUAL: + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.argument, + }: + return NO + + elif prevp.type == token.DOUBLESTAR: + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.dictsetmaker, + }: + return NO + + +############################################################################### +# SECTION BECAUSE SECTIONS +############################################################################### + + +def g(): + NO = '' + SPACE = ' ' + DOUBLESPACE = ' ' + + t = leaf.type + p = leaf.parent + v = leaf.value + + # Comment because comments + + if t in ALWAYS_NO_SPACE: + pass + if t == token.COMMENT: + return DOUBLESPACE + + # Another comment because more comments + assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}" + + prev = leaf.prev_sibling + if not prev: + prevp = preceding_leaf(p) + + if not prevp or prevp.type in OPENING_BRACKETS: + # Start of the line or a bracketed expression. + # More than one line for the comment. + return NO + + if prevp.type == token.EQUAL: + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.argument, + }: + return NO + + +# output + + +def f(): + NO = '' + SPACE = ' ' + DOUBLESPACE = ' ' + + t = leaf.type + p = leaf.parent # trailing comment + v = leaf.value + + if t in ALWAYS_NO_SPACE: + pass + if t == token.COMMENT: # another trailing comment + return DOUBLESPACE + + assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}" + + prev = leaf.prev_sibling + if not prev: + prevp = preceding_leaf(p) + if not prevp or prevp.type in OPENING_BRACKETS: + return NO + + if prevp.type == token.EQUAL: + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.argument, + }: + return NO + + elif prevp.type == token.DOUBLESTAR: + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.dictsetmaker, + }: + return NO + + +############################################################################### +# SECTION BECAUSE SECTIONS +############################################################################### +def g(): + NO = '' + SPACE = ' ' + DOUBLESPACE = ' ' + + t = leaf.type + p = leaf.parent + v = leaf.value + + # Comment because comments + if t in ALWAYS_NO_SPACE: + pass + if t == token.COMMENT: + return DOUBLESPACE + + # Another comment because more comments + assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}" + + prev = leaf.prev_sibling + if not prev: + prevp = preceding_leaf(p) + + if not prevp or prevp.type in OPENING_BRACKETS: + # Start of the line or a bracketed expression. + # More than one line for the comment. + return NO + + if prevp.type == token.EQUAL: + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.argument, + }: + return NO diff --git a/tests/test_black.py b/tests/test_black.py index 225ece6..ee883ec 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -171,6 +171,14 @@ class BlackTestCase(unittest.TestCase): black.assert_equivalent(source, actual) black.assert_stable(source, actual, line_length=ll) + @patch("black.dump_to_file", dump_to_stderr) + def test_empty_lines(self) -> None: + source, expected = read_data('empty_lines') + actual = fs(source) + self.assertFormatEqual(expected, actual) + black.assert_equivalent(source, actual) + black.assert_stable(source, actual, line_length=ll) + def test_report(self) -> None: report = black.Report() out_lines = [] -- 2.39.5