Repository: google/yapf
Pull request #592 (2018-08-01 17:26:53)

Adds an INDENT_BLANK_LINES option.
Adds an INDENT_BLANK_LINES option in order to allow indentation to continue on blank lines. Might be useful when trying to copy and paste pieces of code directly into a Python interpreter.


Patch:
diff --git a/CHANGELOG b/CHANGELOG
index 076997f..83b26c9 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,6 +2,11 @@
 # All notable changes to this project will be documented in this file.
 # This project adheres to [Semantic Versioning](http://semver.org/).
 
+## [0.25.0] UNRELEASED
+### Added
+- Added `INDENT_BLANK_LINES` knob to select whether the blank lines are empty
+  or indented consistently with the current block.
+
 ## [0.24.0] 2018-09-07
 ### Added
 - Added 'SPLIT_BEFORE_DOT' knob to support "builder style" calls. The "builder
diff --git a/README.rst b/README.rst
index b91a71f..366a80e 100644
--- a/README.rst
+++ b/README.rst
@@ -448,6 +448,9 @@ Knobs
 ``INDENT_WIDTH``
     The number of columns to use for indentation.
 
+``INDENT_BLANK_LINES``
+    Set to ``True`` to prefer indented blank lines rather than empty
+
 ``JOIN_MULTIPLE_LINES``
     Join short lines into one line. E.g., single line ``if`` statements.
 
diff --git a/yapf/yapflib/format_token.py b/yapf/yapflib/format_token.py
index f1ca499..9b428b3 100644
--- a/yapf/yapflib/format_token.py
+++ b/yapf/yapflib/format_token.py
@@ -139,6 +139,15 @@ class FormatToken(object):
     else:
       self.value = self.node.value
 
+  @property
+  def formatted_whitespace_prefix(self):
+    if style.Get('INDENT_BLANK_LINES'):
+      without_newlines = self.whitespace_prefix.lstrip('\n')
+      height = len(self.whitespace_prefix) - len(without_newlines)
+      if height:
+        return ('\n' + without_newlines) * height
+    return self.whitespace_prefix
+
   def AddWhitespacePrefix(self, newlines_before, spaces=0, indent_level=0):
     """Register a token's whitespace prefix.
 
diff --git a/yapf/yapflib/reformatter.py b/yapf/yapflib/reformatter.py
index ebbff56..7d26038 100644
--- a/yapf/yapflib/reformatter.py
+++ b/yapf/yapflib/reformatter.py
@@ -253,7 +253,7 @@ def _FormatFinalLines(final_lines, verify):
     formatted_line = []
     for tok in line.tokens:
       if not tok.is_pseudo_paren:
-        formatted_line.append(tok.whitespace_prefix)
+        formatted_line.append(tok.formatted_whitespace_prefix)
         formatted_line.append(tok.value)
       else:
         if (not tok.next_token.whitespace_prefix.startswith('\n') and
diff --git a/yapf/yapflib/style.py b/yapf/yapflib/style.py
index ecead0a..87f1b54 100644
--- a/yapf/yapflib/style.py
+++ b/yapf/yapflib/style.py
@@ -154,6 +154,8 @@ _STYLE_HELP = dict(
         }"""),
     INDENT_WIDTH=textwrap.dedent("""\
       The number of columns to use for indentation."""),
+    INDENT_BLANK_LINES=textwrap.dedent("""\
+      Indent blank lines."""),
     JOIN_MULTIPLE_LINES=textwrap.dedent("""\
       Join short lines into one line. E.g., single line 'if' statements."""),
     NO_SPACES_AROUND_SELECTED_BINARY_OPERATORS=textwrap.dedent("""\
@@ -291,6 +293,7 @@ def CreatePEP8Style():
       I18N_FUNCTION_CALL='',
       INDENT_DICTIONARY_VALUE=False,
       INDENT_WIDTH=4,
+      INDENT_BLANK_LINES=False,
       JOIN_MULTIPLE_LINES=True,
       NO_SPACES_AROUND_SELECTED_BINARY_OPERATORS=set(),
       SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET=True,
@@ -444,6 +447,7 @@ _STYLE_OPTION_VALUE_CONVERTER = dict(
     I18N_FUNCTION_CALL=_StringListConverter,
     INDENT_DICTIONARY_VALUE=_BoolConverter,
     INDENT_WIDTH=int,
+    INDENT_BLANK_LINES=_BoolConverter,
     JOIN_MULTIPLE_LINES=_BoolConverter,
     NO_SPACES_AROUND_SELECTED_BINARY_OPERATORS=_StringSetConverter,
     SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET=_BoolConverter,


Test patch:
diff --git a/yapftests/reformatter_basic_test.py b/yapftests/reformatter_basic_test.py
index 8728a7e..57a45d6 100644
--- a/yapftests/reformatter_basic_test.py
+++ b/yapftests/reformatter_basic_test.py
@@ -134,6 +134,40 @@ class BasicReformatterTest(yapf_test_helper.YAPFTest):
     uwlines = yapf_test_helper.ParseAndUnwrap(unformatted_code)
     self.assertCodeEqual(expected_formatted_code, reformatter.Reformat(uwlines))
 
+  def testIndentBlankLines(self):
+    try:
+      style.SetGlobalStyle(
+          style.CreateStyleFromConfig(
+              '{based_on_style: chromium, indent_blank_lines: true}'))
+      unformatted_code = textwrap.dedent("""\
+          class foo(object):
+
+            def foobar(self):
+
+              pass
+
+            def barfoo(self, x, y):  # bar
+
+              if x:
+
+                return y
+
+
+          def bar():
+
+            return 0
+          """)
+      expected_formatted_code = """class foo(object):\n  \n  def foobar(self):\n    \n    pass\n  \n  def barfoo(self, x, y):  # bar\n    \n    if x:\n      \n      return y\n\n\ndef bar():\n  \n  return 0\n"""
+      uwlines = yapf_test_helper.ParseAndUnwrap(unformatted_code)
+      self.assertCodeEqual(expected_formatted_code,
+                           reformatter.Reformat(uwlines))
+    finally:
+      style.SetGlobalStyle(style.CreateChromiumStyle())
+
+    unformatted_code, expected_formatted_code = expected_formatted_code, unformatted_code
+    uwlines = yapf_test_helper.ParseAndUnwrap(unformatted_code)
+    self.assertCodeEqual(expected_formatted_code, reformatter.Reformat(uwlines))
+
   def testMultipleUgliness(self):
     unformatted_code = textwrap.dedent("""\
         x = {  'a':37,'b':42,