Repository: google/yapf
Pull request #791 (2020-01-04 08:14:24)

extend CONTINUATION_ALIGN_STYLE for space indentation
This PR make `CONTINUATION_ALIGN_STYLE=FIXED` and `CONTINUATION_ALIGN_STYLE=VALIGN-RIGHT` option available for indenting with space.

Ref: #769 

Compare to previous PR (#774) this one should solve the extra line break issue.

--------

Changes made:

* let new line indent generation method in format decision be continuation
  align style aware
* simplify tab replacement in format token as styling logic moved to format decision mostly

coveralls: 
[![Coverage Status](https://coveralls.io/builds/27912297/badge)](https://coveralls.io/builds/27912297)

Coverage decreased (-0.09%) to 96.12% when pulling **424973824704c02b1f42415c8015bb0f8fc45344 on yinyin:cont-align-style-w-space-3** into **111d4249b9c2bb47b29385b4af8967f9028e44bf on google:master**.


Patch:
diff --git a/CHANGELOG b/CHANGELOG
index 0619298..929b560 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -15,6 +15,8 @@
   slice operator.
 ### Changed
 - Renamed "chromium" style to "yapf". Chromium will now use PEP-8 directly.
+- `CONTINUATION_ALIGN_STYLE` with `FIXED` or `VALIGN-RIGHT` now works with
+  space indentation.
 ### Fixed
 - Honor a disable directive at the end of a multiline comment.
 - Don't require splitting before comments in a list when
diff --git a/README.rst b/README.rst
index 316d8a0..e2db751 100644
--- a/README.rst
+++ b/README.rst
@@ -434,15 +434,12 @@ Knobs
     - ``SPACE``: Use spaces for continuation alignment. This is default
       behavior.
     - ``FIXED``: Use fixed number (CONTINUATION_INDENT_WIDTH) of columns
-      (ie: CONTINUATION_INDENT_WIDTH/INDENT_WIDTH tabs) for continuation
-      alignment.
-    - ``VALIGN-RIGHT``: Vertically align continuation lines with indent
-      characters. Slightly right (one more indent character) if cannot
+      (ie: CONTINUATION_INDENT_WIDTH/INDENT_WIDTH tabs or CONTINUATION_INDENT_WIDTH
+      spaces) for continuation alignment.
+    - ``VALIGN-RIGHT``: Vertically align continuation lines to multiple of
+      INDENT_WIDTH columns. Slightly right (one tab or a few spaces) if cannot
       vertically align continuation lines with indent characters.
 
-      For options ``FIXED``, and ``VALIGN-RIGHT`` are only available when
-      ``USE_TABS`` is enabled.
-
 ``CONTINUATION_INDENT_WIDTH``
     Indent width used for line continuations.
 
diff --git a/yapf/yapflib/format_decision_state.py b/yapf/yapflib/format_decision_state.py
index e305784..577ae10 100644
--- a/yapf/yapflib/format_decision_state.py
+++ b/yapf/yapflib/format_decision_state.py
@@ -921,6 +921,18 @@ class FormatDecisionState(object):
 
     return penalty
 
+  def _IndentWithContinuationAlignStyle(self, column):
+    if column == 0:
+      return column
+    align_style = style.Get('CONTINUATION_ALIGN_STYLE')
+    if align_style == 'FIXED':
+      return ((self.line.depth * style.Get('INDENT_WIDTH')) +
+              style.Get('CONTINUATION_INDENT_WIDTH'))
+    if align_style == 'VALIGN-RIGHT':
+      indent_width = style.Get('INDENT_WIDTH')
+      return indent_width * int((column + indent_width - 1) / indent_width)
+    return column
+
   def _GetNewlineColumn(self):
     """Return the new column on the newline."""
     current = self.next_token
@@ -934,8 +946,11 @@ class FormatDecisionState(object):
     elif current.spaces_required_before > 2 or self.line.disable:
       return current.spaces_required_before
 
+    cont_aligned_indent = self._IndentWithContinuationAlignStyle(
+        top_of_stack.indent)
+
     if current.OpensScope():
-      return top_of_stack.indent if self.paren_level else self.first_indent
+      return cont_aligned_indent if self.paren_level else self.first_indent
 
     if current.ClosesScope():
       if (previous.OpensScope() or
@@ -973,7 +988,7 @@ class FormatDecisionState(object):
            format_token.Subtype.PARAMETER_START in previous.subtypes)):
         return top_of_stack.indent + style.Get('CONTINUATION_INDENT_WIDTH')
 
-    return top_of_stack.indent
+    return cont_aligned_indent
 
   def _FitsOnLine(self, start, end):
     """Determines if line between start and end can fit on the current line."""
diff --git a/yapf/yapflib/format_token.py b/yapf/yapflib/format_token.py
index e6af942..0ed983f 100644
--- a/yapf/yapflib/format_token.py
+++ b/yapf/yapflib/format_token.py
@@ -62,25 +62,21 @@ class Subtype(object):
   PARAMETER_STOP = 26
 
 
-def _TabbedContinuationAlignPadding(spaces, align_style, tab_width,
-                                    continuation_indent_width):
+def _TabbedContinuationAlignPadding(spaces, align_style, tab_width):
   """Build padding string for continuation alignment in tabbed indentation.
 
   Arguments:
     spaces: (int) The number of spaces to place before the token for alignment.
     align_style: (str) The alignment style for continuation lines.
     tab_width: (int) Number of columns of each tab character.
-    continuation_indent_width: (int) Indent columns for line continuations.
 
   Returns:
     A padding string for alignment with style specified by align_style option.
   """
-  if align_style == 'FIXED':
+  if align_style in ('FIXED', 'VALIGN-RIGHT'):
     if spaces > 0:
-      return '\t' * int(continuation_indent_width / tab_width)
+      return '\t' * int((spaces + tab_width - 1) / tab_width)
     return ''
-  elif align_style == 'VALIGN-RIGHT':
-    return '\t' * int((spaces + tab_width - 1) / tab_width)
   return ' ' * spaces
 
 
@@ -171,7 +167,7 @@ class FormatToken(object):
       if newlines_before > 0:
         indent_before = '\t' * indent_level + _TabbedContinuationAlignPadding(
             spaces, style.Get('CONTINUATION_ALIGN_STYLE'),
-            style.Get('INDENT_WIDTH'), style.Get('CONTINUATION_INDENT_WIDTH'))
+            style.Get('INDENT_WIDTH'))
       else:
         indent_before = '\t' * indent_level + ' ' * spaces
     else:
diff --git a/yapf/yapflib/style.py b/yapf/yapflib/style.py
index 4e08365..7cb3180 100644
--- a/yapf/yapflib/style.py
+++ b/yapf/yapflib/style.py
@@ -128,14 +128,11 @@ _STYLE_HELP = dict(
 
       - SPACE: Use spaces for continuation alignment. This is default behavior.
       - FIXED: Use fixed number (CONTINUATION_INDENT_WIDTH) of columns
-        (ie: CONTINUATION_INDENT_WIDTH/INDENT_WIDTH tabs) for continuation
-        alignment.
-      - VALIGN-RIGHT: Vertically align continuation lines with indent
-        characters. Slightly right (one more indent character) if cannot
-        vertically align continuation lines with indent characters.
-
-      Options FIXED and VALIGN-RIGHT are only available when USE_TABS is
-      enabled."""),
+        (ie: CONTINUATION_INDENT_WIDTH/INDENT_WIDTH tabs or
+        CONTINUATION_INDENT_WIDTH spaces) for continuation alignment.
+      - VALIGN-RIGHT: Vertically align continuation lines to multiple of
+        INDENT_WIDTH columns. Slightly right (one tab or a few spaces) if
+        cannot vertically align continuation lines with indent characters."""),
     CONTINUATION_INDENT_WIDTH=textwrap.dedent("""\
       Indent width used for line continuations."""),
     DEDENT_CLOSING_BRACKETS=textwrap.dedent("""\


Test patch:
diff --git a/yapftests/format_token_test.py b/yapftests/format_token_test.py
index 2f6f973..b4c7151 100644
--- a/yapftests/format_token_test.py
+++ b/yapftests/format_token_test.py
@@ -26,40 +26,40 @@ class TabbedContinuationAlignPaddingTest(unittest.TestCase):
   def testSpace(self):
     align_style = 'SPACE'
 
-    pad = format_token._TabbedContinuationAlignPadding(0, align_style, 2, 4)
+    pad = format_token._TabbedContinuationAlignPadding(0, align_style, 2)
     self.assertEqual(pad, '')
 
-    pad = format_token._TabbedContinuationAlignPadding(2, align_style, 2, 4)
+    pad = format_token._TabbedContinuationAlignPadding(2, align_style, 2)
     self.assertEqual(pad, ' ' * 2)
 
-    pad = format_token._TabbedContinuationAlignPadding(5, align_style, 2, 4)
+    pad = format_token._TabbedContinuationAlignPadding(5, align_style, 2)
     self.assertEqual(pad, ' ' * 5)
 
   def testFixed(self):
     align_style = 'FIXED'
 
-    pad = format_token._TabbedContinuationAlignPadding(0, align_style, 4, 8)
+    pad = format_token._TabbedContinuationAlignPadding(0, align_style, 4)
     self.assertEqual(pad, '')
 
-    pad = format_token._TabbedContinuationAlignPadding(2, align_style, 4, 8)
-    self.assertEqual(pad, '\t' * 2)
+    pad = format_token._TabbedContinuationAlignPadding(2, align_style, 4)
+    self.assertEqual(pad, '\t')
 
-    pad = format_token._TabbedContinuationAlignPadding(5, align_style, 4, 8)
+    pad = format_token._TabbedContinuationAlignPadding(5, align_style, 4)
     self.assertEqual(pad, '\t' * 2)
 
   def testVAlignRight(self):
     align_style = 'VALIGN-RIGHT'
 
-    pad = format_token._TabbedContinuationAlignPadding(0, align_style, 4, 8)
+    pad = format_token._TabbedContinuationAlignPadding(0, align_style, 4)
     self.assertEqual(pad, '')
 
-    pad = format_token._TabbedContinuationAlignPadding(2, align_style, 4, 8)
+    pad = format_token._TabbedContinuationAlignPadding(2, align_style, 4)
     self.assertEqual(pad, '\t')
 
-    pad = format_token._TabbedContinuationAlignPadding(4, align_style, 4, 8)
+    pad = format_token._TabbedContinuationAlignPadding(4, align_style, 4)
     self.assertEqual(pad, '\t')
 
-    pad = format_token._TabbedContinuationAlignPadding(5, align_style, 4, 8)
+    pad = format_token._TabbedContinuationAlignPadding(5, align_style, 4)
     self.assertEqual(pad, '\t' * 2)
 
 
diff --git a/yapftests/yapf_test.py b/yapftests/yapf_test.py
index 9bd09ac..d685633 100644
--- a/yapftests/yapf_test.py
+++ b/yapftests/yapf_test.py
@@ -1269,8 +1269,8 @@ def foo_function(arg1, arg2, arg3):
   return ['hello', 'world',]
 """
     expected_formatted_code = """\
-def foo_function(arg1, arg2,
-		arg3):
+def foo_function(
+		arg1, arg2, arg3):
 	return [
 			'hello',
 			'world',
@@ -1312,6 +1312,60 @@ COLUMN_LIMIT=32
 INDENT_WIDTH=4
 CONTINUATION_INDENT_WIDTH=8
 CONTINUATION_ALIGN_STYLE = valign-right
+"""
+    with utils.TempFileContents(self.test_tmpdir, style_contents) as stylepath:
+      self.assertYapfReformats(
+          unformatted_code,
+          expected_formatted_code,
+          extra_options=['--style={0}'.format(stylepath)])
+
+  def testUseSpacesContinuationAlignStyleFixed(self):
+    unformatted_code = """\
+def foo_function(arg1, arg2, arg3):
+  return ['hello', 'world',]
+"""
+    expected_formatted_code = """\
+def foo_function(
+        arg1, arg2, arg3):
+    return [
+            'hello',
+            'world',
+    ]
+"""
+    style_contents = u"""\
+[style]
+based_on_style = chromium
+COLUMN_LIMIT=32
+INDENT_WIDTH=4
+CONTINUATION_INDENT_WIDTH=8
+CONTINUATION_ALIGN_STYLE = fixed
+"""
+    with utils.TempFileContents(self.test_tmpdir, style_contents) as stylepath:
+      self.assertYapfReformats(
+          unformatted_code,
+          expected_formatted_code,
+          extra_options=['--style={0}'.format(stylepath)])
+
+  def testUseSpacesContinuationAlignStyleVAlignRight(self):
+    unformatted_code = """\
+def foo_function(arg1, arg2, arg3):
+  return ['hello', 'world',]
+"""
+    expected_formatted_code = """\
+def foo_function(arg1, arg2,
+                    arg3):
+    return [
+            'hello',
+            'world',
+    ]
+"""
+    style_contents = u"""\
+[style]
+based_on_style = chromium
+COLUMN_LIMIT=32
+INDENT_WIDTH=4
+CONTINUATION_INDENT_WIDTH=8
+CONTINUATION_ALIGN_STYLE = valign-right
 """
     with utils.TempFileContents(self.test_tmpdir, style_contents) as stylepath:
       self.assertYapfReformats(