Repository: spyder-ide/spyder

Issue:
Code folding is wrong on commented lines
The indentation of lines fully commented break the blocs logic. eg:

```
def foo():
# comment
  pass
```

the folding triangle appear on '#'.
Probably they should simply be ignored.



Discussion:
The actual code folding is based in indentation, this is somehow duplicated of #2845, although a better solution will be to implement a fold detector based on tokens (#4432)
Maybe you just have to treat the commented lines differently allowing indentation change for them without breaking the current block. Then possibly add a dedicated block on successive commented lines.

Resolving patch:
diff --git a/spyder/widgets/sourcecode/api/folding.py b/spyder/widgets/sourcecode/api/folding.py
--- a/spyder/widgets/sourcecode/api/folding.py
+++ b/spyder/widgets/sourcecode/api/folding.py
@@ -91,8 +91,9 @@ def process_block(self, current_block, previous_block, text):
         :param text: current block text
         """
         prev_fold_level = TextBlockHelper.get_fold_lvl(previous_block)
-        if text.strip() == '':
-            # blank line always have the same level as the previous line
+        if text.strip() == '' or self.editor.is_comment(current_block):
+            # blank or comment line always have the same level
+            # as the previous line
             fold_level = prev_fold_level
         else:
             fold_level = self.detect_fold_level(
@@ -103,32 +104,34 @@ def process_block(self, current_block, previous_block, text):
         prev_fold_level = TextBlockHelper.get_fold_lvl(previous_block)
 
         if fold_level > prev_fold_level:
-            # apply on previous blank lines
+            # apply on previous blank or comment lines
             block = current_block.previous()
-            while block.isValid() and block.text().strip() == '':
+            while block.isValid() and (block.text().strip() == ''
+                                       or self.editor.is_comment(block)):
                 TextBlockHelper.set_fold_lvl(block, fold_level)
                 block = block.previous()
             TextBlockHelper.set_fold_trigger(
                 block, True)
 
         # update block fold level
-        if text.strip():
+        if text.strip() and not self.editor.is_comment(previous_block):
             TextBlockHelper.set_fold_trigger(
                 previous_block, fold_level > prev_fold_level)
         TextBlockHelper.set_fold_lvl(current_block, fold_level)
 
         # user pressed enter at the beginning of a fold trigger line
-        # the previous blank line will keep the trigger state and the new line
-        # (which actually contains the trigger) must use the prev state (
-        # and prev state must then be reset).
+        # the previous blank or comment line will keep the trigger state
+        # and the new line (which actually contains the trigger) must use
+        # the prev state (and prev state must then be reset).
         prev = current_block.previous()  # real prev block (may be blank)
-        if (prev and prev.isValid() and prev.text().strip() == '' and
+        if (prev and prev.isValid() and
+            (prev.text().strip() == '' or self.editor.is_comment(prev)) and
                 TextBlockHelper.is_fold_trigger(prev)):
             # prev line has the correct trigger fold state
             TextBlockHelper.set_collapsed(
                 current_block, TextBlockHelper.is_collapsed(
                     prev))
-            # make empty line not a trigger
+            # make empty or comment line not a trigger
             TextBlockHelper.set_fold_trigger(prev, False)
             TextBlockHelper.set_collapsed(prev, False)
 
diff --git a/spyder/widgets/sourcecode/codeeditor.py b/spyder/widgets/sourcecode/codeeditor.py
--- a/spyder/widgets/sourcecode/codeeditor.py
+++ b/spyder/widgets/sourcecode/codeeditor.py
@@ -1927,6 +1927,16 @@ def toggle_comment(self):
         else:
             self.comment()
 
+    def is_comment(self, block):
+        """Detect inline comments.
+
+        Return True if the block is an inline comment.
+        """
+        if block is None:
+            return False
+        text = to_text_string(block.text()).lstrip()
+        return text.startswith(self.comment_string)
+
     def comment(self):
         """Comment current line or selection."""
         self.add_prefix(self.comment_string)
diff --git a/spyder/widgets/sourcecode/folding.py b/spyder/widgets/sourcecode/folding.py
--- a/spyder/widgets/sourcecode/folding.py
+++ b/spyder/widgets/sourcecode/folding.py
@@ -215,11 +215,13 @@ def detect_fold_level(self, prev_block, block):
         prev_lvl = TextBlockHelper().get_fold_lvl(prev_block)
         # round down to previous indentation guide to ensure contiguous block
         # fold level evolution.
-
-        if prev_lvl and prev_block is not None:
+        indent_len = 0
+        if (prev_lvl and prev_block is not None and
+           not self.editor.is_comment(prev_block)):
+            # ignore commented lines (could have arbitary indentation)
             prev_text = prev_block.text()
             indent_len = (len(prev_text) - len(prev_text.lstrip())) // prev_lvl
-        else:
+        if indent_len == 0:
             indent_len = len(self.editor.indent_chars)
 
         return (len(text) - len(text.lstrip())) // indent_len


Test patch:
diff --git a/spyder/widgets/sourcecode/tests/test_folding.py b/spyder/widgets/sourcecode/tests/test_folding.py
--- a/spyder/widgets/sourcecode/tests/test_folding.py
+++ b/spyder/widgets/sourcecode/tests/test_folding.py
@@ -36,6 +36,8 @@ def get_fold_levels():
             '        self.b = 3\n'
             '\n'
             '    def other_method(self):\n'  # fold-block level-1
+            '\n' # a blank line (should be ignored)
+            '  # a comment with arbitrary indentation\n' # should be ignored
             '         a = (1,\n'  # fold-block level-2
             '              2,\n'
             '              3)\n'
@@ -50,6 +52,6 @@ def test_simple_folding(get_fold_levels):
     assert get_fold_levels == [[2, 0, 'V'], 
                                  [6, 1, 'V'],
                                  [9, 1, 'V'],
-                                 [10, 2, 'V']]
+                                 [12, 2, 'V']]