]> git.madduck.net Git - etc/vim.git/commitdiff

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Don't remove single empty lines outside of bracketed expressions
authorŁukasz Langa <lukasz@langa.pl>
Wed, 21 Mar 2018 01:15:20 +0000 (18:15 -0700)
committerŁukasz Langa <lukasz@langa.pl>
Wed, 21 Mar 2018 01:54:19 +0000 (18:54 -0700)
Fixes #19

README.md
black.py
tests/comments2.py
tests/empty_lines.py [new file with mode: 0644]
tests/test_black.py

index 1b833089bbe489591547a6056f0eb03e48f9f839..e4ccef20fc75df350b9a4c3f9fddb3ba8b4a4793 100644 (file)
--- a/README.md
+++ b/README.md
@@ -259,6 +259,9 @@ More details can be found in [CONTRIBUTING](CONTRIBUTING.md).
 
 ### 18.3a3
 
 
 ### 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
 * added ability to pipe formatting from stdin to stdin (#25)
 
 * restored ability to format code with legacy usage of `async` as
index 74329d2389901323d2c743f9a55758c6107417f5..8a4eb05e5ad71cf04976c913a0f8bac6631bae7f 100644 (file)
--- 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):
 
     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):
             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:
                     # 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)
                     # 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()
             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:
                 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)
     # 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:
     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)
     # 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:
             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)
             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, ','))
             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
 
 
         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)
         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 = ''
 
 
     leaf.prefix = ''
 
index 6689c886e2c868a8d8985f158758a6555817e70c..da4f7a16a06db100275cd6c15608db613464c049 100644 (file)
@@ -159,6 +159,7 @@ short
 """,
         arg3=True,
     )
 """,
         arg3=True,
     )
+
     ############################################################################
     call2(
         # short
     ############################################################################
     call2(
         # short
diff --git a/tests/empty_lines.py b/tests/empty_lines.py
new file mode 100644 (file)
index 0000000..fe86b38
--- /dev/null
@@ -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
index 225ece6f474e5f66ff7efdc17da8e9f906e516f0..ee883ec011d1be2ea9015cc48fdbe95843a358e4 100644 (file)
@@ -171,6 +171,14 @@ class BlackTestCase(unittest.TestCase):
         black.assert_equivalent(source, actual)
         black.assert_stable(source, actual, line_length=ll)
 
         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 = []
     def test_report(self) -> None:
         report = black.Report()
         out_lines = []