Repository: spyder-ide/spyder

Issue:
Outline Explorer does not properly handle async functions and methods
<!--- **PLEASE READ:** When submitting here, please ensure you've completed the following checklist and checked the boxes to confirm. Issue reports without it may be closed. Thanks! --->

### Issue Report Checklist

* [x] Searched the [issues page](https://github.com/spyder-ide/spyder/issues?q=is%3Aissue) for similar reports
* [x] Read the relevant sections of the [Spyder Troubleshooting Guide](https://github.com/spyder-ide/spyder/wiki/Troubleshooting-Guide-and-FAQ) and followed its advice
* [x] Reproduced the issue after updating with ``conda update spyder`` (or ``pip``, if not using Anaconda)
* [ ] Could not reproduce inside ``jupyter qtconsole`` (if console-related)
* [x] Tried basic troubleshooting (if a bug/error)
    * [x] Restarted Spyder
    * [ ] Reset preferences with ``spyder --reset``
    * [ ] Reinstalled the latest version of [Anaconda](https://www.anaconda.com/download/)
    * [x] Tried the other applicable steps from the Troubleshooting Guide
* [x] Completed the **Problem Description**, **Steps to Reproduce** and **Version** sections below


## Problem Description

The outline explorer does not properly display async methods and functions, placing them wrongly in the object tree or simply ignoring them.

### What steps reproduce the problem?

1. Create a new file.
2. Paste this example code, or otherwise write code using `async def`
```py
import asyncio

class Foo():
    def __init__(self):
        self.a = 10

    async def foo(self):
        while self.a:
            await asyncio.sleep(self.a)
            self.a -= 1

    async def bar(self):
        self.a = 10

async def baz():
    pass
```
3. Save the file to make the outline explorer update.

### What is the expected output? What do you see instead?

The expected output is to see `Foo` and `baz` as a top level class and function, with `__init__`, `foo` and `bar` being methods on `Foo`.  
Here is the example code with the `async` and `await` statements removed. This is working as intended.
![image](https://user-images.githubusercontent.com/43568797/51032567-6b3ec980-1598-11e9-8f9f-cc53dbfbea36.png)


What happens instead is that some async functions are being ignored, others are being placed wrong, and async methods are being classified as functions. This screenshot is the example code:
![image](https://user-images.githubusercontent.com/43568797/51032177-ff0f9600-1596-11e9-9089-b5fc2c7a98a7.png)


## Versions
<!--- You can get this information from Help > About Spyder...
or (if Spyder won't launch) the "conda list" command
from the Anaconda Prompt/Terminal/command line. --->

* Spyder version: 3.3.2
* Python version: 3.7.0 32-bit
* Qt version: 5.9.3
* PyQt version: 5.9.2
* Operating System name/version: Windows 10

### Dependencies
<!--- Please go to the menu entry Help > Dependencies,
press the Copy to clipboard button and paste below --->

```
IPython >=4.0     :  6.5.0 (OK)
cython >=0.21     :  0.28.5 (OK)
jedi >=0.9.0      :  0.12.1 (OK)
matplotlib >=2.0.0:  3.0.2 (OK)
nbconvert >=4.0   :  5.4.0 (OK)
numpy >=1.7       :  1.15.2 (OK)
pandas >=0.13.1   :  0.23.4 (OK)
pycodestyle >=2.3 :  2.4.0 (OK)
pyflakes >=0.6.0  :  2.0.0 (OK)
pygments >=2.0    :  2.2.0 (OK)
pylint >=0.25     :  2.1.1 (OK)
qtconsole >=4.2.0 :  4.4.1 (OK)
rope >=0.9.4      :  0.11.0 (OK)
sphinx >=0.6.6    :  1.8.0 (OK)
sympy >=0.7.3     :  None (NOK)
```



Discussion:

Resolving patch:
diff --git a/spyder/plugins/outlineexplorer/widgets.py b/spyder/plugins/outlineexplorer/widgets.py
--- a/spyder/plugins/outlineexplorer/widgets.py
+++ b/spyder/plugins/outlineexplorer/widgets.py
@@ -466,8 +466,6 @@ def populate_branch(self, editor, root_item, tree_cache=None):
             if previous_level is not None:
                 if level == previous_level:
                     pass
-                elif level > previous_level+4: # Invalid indentation
-                    continue
                 elif level > previous_level:
                     ancestors.append((previous_item, previous_level))
                 else:
diff --git a/spyder/utils/syntaxhighlighters.py b/spyder/utils/syntaxhighlighters.py
--- a/spyder/utils/syntaxhighlighters.py
+++ b/spyder/utils/syntaxhighlighters.py
@@ -501,7 +501,8 @@ def highlight_block(self, text):
                                                    self.formats["definition"])
                                     oedata = OutlineExplorerData()
                                     oedata.text = to_text_string(text)
-                                    oedata.fold_level = start
+                                    oedata.fold_level = (len(text)
+                                                         - len(text.lstrip()))
                                     oedata.def_type = self.DEF_TYPES[
                                                         to_text_string(value)]
                                     oedata.def_name = text[start1:end1]


Test patch:
diff --git a/spyder/plugins/outlineexplorer/tests/test_widgets.py b/spyder/plugins/outlineexplorer/tests/test_widgets.py
--- a/spyder/plugins/outlineexplorer/tests/test_widgets.py
+++ b/spyder/plugins/outlineexplorer/tests/test_widgets.py
@@ -89,7 +89,7 @@ def inside(x):
     class Class2(x):
         def __init__(x):
             return x
-        def medthod1(x):
+        async def medthod1(x):
             if x:
                 return x