Repository: google/yapf

Issue:
DEDENT_CLOSING_BRACKETS does not seem to work if arguments fit in line
I am getting somewhat inconsistent formatting on code like this:

```python
def long_function_name(argument1: Tuple[str, int], argument2: List[str]) -> Iterator[str]:
    pass

def ever_longer_function_name(argument1: Tuple[str, int], argument2: List[str]) -> Iterator[str]:
    pass
```

My `setup.cfg`:
```ini
[yapf]
based_on_style = pep8
coalesce_brackets = True
dedent_closing_brackets = True
split_penalty_after_opening_bracket = 0
split_before_first_argument = true
```

The result is this:
```python
def long_function_name(argument1: Tuple[str, int],
                       argument2: List[str]) -> Iterator[str]:
    pass


def ever_longer_function_name(
        argument1: Tuple[str, int], argument2: List[str]
) -> Iterator[str]:
    pass
```

However, i would prefer the second style for the first example as well. I was expecting the `split_before_first_argument` option to have exactly that effect, but it does not seem to work.

With type annotations, the function signatures can get significant length other than their arguments. so this happens quite frequently in my code base. This issue seems to happen if the arguments fit in one line, but the full signature does not. If the arguments are longer than one line, it works fine.

I am using version 0.14.0.

Discussion:

Patch:
diff --git a/CHANGELOG b/CHANGELOG
index 83b26c9..698e6ea 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -6,6 +6,10 @@
 ### Added
 - Added `INDENT_BLANK_LINES` knob to select whether the blank lines are empty
   or indented consistently with the current block.
+### Fixed
+- Correctly determine if a scope is the last in line. It avoids a wrong
+  computation of the line end when determining if it must split after the
+  opening bracket with `DEDENT_CLOSING_BRACKETS` enabled.
 
 ## [0.24.0] 2018-09-07
 ### Added
diff --git a/yapf/yapflib/format_decision_state.py b/yapf/yapflib/format_decision_state.py
index cd6a729..40b4571 100644
--- a/yapf/yapflib/format_decision_state.py
+++ b/yapf/yapflib/format_decision_state.py
@@ -930,6 +930,7 @@ def _IsFunctionDefinition(current):
 
 
 def _IsLastScopeInLine(current):
+  current = current.matching_bracket
   while current:
     current = current.next_token
     if current and current.OpensScope():


Test patch:
diff --git a/yapftests/reformatter_basic_test.py b/yapftests/reformatter_basic_test.py
index 57a45d6..3de71a8 100644
--- a/yapftests/reformatter_basic_test.py
+++ b/yapftests/reformatter_basic_test.py
@@ -2558,6 +2558,37 @@ x = [1, 2, 3, 4, 5, 6, 7,]
     finally:
       style.SetGlobalStyle(style.CreateChromiumStyle())
 
+  def testDedentClosingBracketsWithTypeAnnotationExceedingLineLength(self):
+    try:
+      style.SetGlobalStyle(
+          style.CreateStyleFromConfig('{based_on_style: chromium,'
+                                      ' dedent_closing_brackets: True}'))
+      unformatted_code = textwrap.dedent("""\
+                def function(first_argument_xxxxxxxxxxxxxxxx=(0,), second_argument=None) -> None:
+                  pass
+
+
+                def function(first_argument_xxxxxxxxxxxxxxxxxxxxxxx=(0,), second_argument=None) -> None:
+                  pass
+                """)
+      expected_formatted_code = textwrap.dedent("""\
+                def function(
+                    first_argument_xxxxxxxxxxxxxxxx=(0,), second_argument=None
+                ) -> None:
+                  pass
+
+
+                def function(
+                    first_argument_xxxxxxxxxxxxxxxxxxxxxxx=(0,), second_argument=None
+                ) -> None:
+                  pass
+                """)
+      uwlines = yapf_test_helper.ParseAndUnwrap(unformatted_code)
+      self.assertCodeEqual(expected_formatted_code,
+                           reformatter.Reformat(uwlines))
+    finally:
+      style.SetGlobalStyle(style.CreateChromiumStyle())
+
 
 if __name__ == '__main__':
   unittest.main()