Repository: spyder-ide/spyder

Issue:
The "Go to cursor position" feature of the outline explorer  is broken in master
## Problem Description

The "Go to cursor position" feature of the outline explorer  is broken in master

### What steps reproduce the problem?

1. Click with the mouse somewhere within a class
2. Click on the `Go to cursor position` in the outline explorer

![image](https://user-images.githubusercontent.com/10170372/44403071-79287b00-a521-11e8-8413-603bc78a5104.png)

### What is the expected output? What do you see instead?

Nothing happens in the outline explorer and an error is raised.

### Paste Traceback/Error Below (if applicable)
<!--- Copy from error dialog or View > Panes > Internal Console --->

```python-traceback
  File "C:\Users\User\spyder\spyder\plugins\outlineexplorer\widgets.py", line 234, in go_to_cursor_position
    line = self.current_editor.get_cursor_line_number()
AttributeError: 'OutlineExplorerProxyEditor' object has no attribute 'get_cursor_line_number'
```

## 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: Spyder 4.0.0.dev0 
* Python version: Python 3.6.5 64bits
* Qt version: Qt 5.9.3
* PyQt version: PyQt5 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 --->

```
pyflakes >=0.6.0  :  1.6.0 (OK)
pycodestyle >=2.3 :  2.3.1 (OK)
pygments >=2.0    :  2.2.0 (OK)
sphinx >=0.6.6    :  1.7.1 (OK)
rope >=0.9.4      :  0.10.7 (OK)
jedi >=0.11.0     :  0.12.1 (OK)
nbconvert >=4.0   :  5.3.1 (OK)
pandas >=0.13.1   :  0.22.0 (OK)
numpy >=1.7       :  1.13.0 (OK)
sympy >=0.7.3     :  1.1.1 (OK)
cython >=0.21     :  0.27.3 (OK)
qtconsole >=4.2.0 :  4.4.0.dev (OK)
IPython >=4.0     :  6.2.1 (OK)
matplotlib >=2.0.0:  2.2.0 (OK)
pylint >=0.25     :  1.8.3 (OK)
```



Discussion:

Resolving patch:
diff --git a/spyder/plugins/outlineexplorer/editor.py b/spyder/plugins/outlineexplorer/editor.py
--- a/spyder/plugins/outlineexplorer/editor.py
+++ b/spyder/plugins/outlineexplorer/editor.py
@@ -21,9 +21,13 @@ def give_focus(self):
         self._editor.clearFocus()
         self._editor.setFocus()
 
+    def get_cursor_line_number(self):
+        return self._editor.get_cursor_line_number()
+
     def get_outlineexplorer_data(self):
         oe_data = self._editor.get_outlineexplorer_data()
-        self._editor.has_cell_separators = oe_data.pop('found_cell_separators', False)
+        self._editor.has_cell_separators = oe_data.pop(
+            'found_cell_separators', False)
         return oe_data
 
     def get_line_count(self):
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
@@ -558,12 +558,11 @@ def toggle_visibility(self, state):
 
     def setup_buttons(self):
         """Setup the buttons of the outline explorer widget toolbar."""
-        fromcursor_btn = create_toolbutton(
-                             self, icon=ima.icon('fromcursor'),
-                             tip=_('Go to cursor position'),
-                             triggered=self.treewidget.go_to_cursor_position)
+        self.fromcursor_btn = create_toolbutton(
+            self, icon=ima.icon('fromcursor'), tip=_('Go to cursor position'),
+            triggered=self.treewidget.go_to_cursor_position)
 
-        buttons = [fromcursor_btn]
+        buttons = [self.fromcursor_btn]
         for action in [self.treewidget.collapse_all_action,
                        self.treewidget.expand_all_action,
                        self.treewidget.restore_action,


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
@@ -12,15 +12,17 @@
 
 # Third party imports
 import pytest
+from qtpy.QtCore import Qt
 
 # Local imports
 from spyder.plugins.outlineexplorer.editor import OutlineExplorerProxyEditor
-from spyder.plugins.outlineexplorer.widgets import (OutlineExplorerWidget,
-    FileRootItem, FunctionItem, CommentItem, CellItem, ClassItem)
+from spyder.plugins.outlineexplorer.widgets import (
+    OutlineExplorerWidget, FileRootItem, FunctionItem, CommentItem,
+    CellItem, ClassItem)
 from spyder.plugins.editor.widgets.codeeditor import CodeEditor
 
 
-text = ("# -*- coding: utf-8 -*-\n"
+TEXT = ("# -*- coding: utf-8 -*-\n"
         "\n"
         "# %% functions\n"
         "\n"
@@ -60,7 +62,7 @@
         "        except AssertionError:\n"
         "            pass")
 
-code = """# -*- coding: utf-8 -*-
+CODE = """# -*- coding: utf-8 -*-
 
     def function0(x):
         return x
@@ -126,42 +128,40 @@ def function6(x):
         x = 'test'
 """
 
-# Qt Test Fixtures
-# --------------------------------
 
+# ---- Qt Test Fixtures
 @pytest.fixture
-def outline_explorer_bot(qtbot):
-    code_editor = CodeEditor(None)
-    code_editor.set_language('py', 'test_outline_explorer.py')
-    code_editor.set_text(text)
+def outlineexplorer_bot(qtbot):
+    def _create_bot(code, filename):
+        code_editor = CodeEditor(None)
+        code_editor.set_language('py', filename)
+        code_editor.set_text(code)
 
-    editor = OutlineExplorerProxyEditor(code_editor,
-                                        'test_outline_explorer.py')
+        editor = OutlineExplorerProxyEditor(code_editor, filename)
 
-    outline_explorer = OutlineExplorerWidget()
-    outline_explorer.set_current_editor(editor, False, False)
+        outlineexplorer = OutlineExplorerWidget()
+        outlineexplorer.set_current_editor(editor, False, False)
+        outlineexplorer.show()
+        outlineexplorer.setFixedSize(400, 350)
 
-    qtbot.addWidget(outline_explorer)
+        qtbot.addWidget(outlineexplorer)
+        return outlineexplorer, qtbot
+    return _create_bot
 
-    return outline_explorer, qtbot
 
-
-# Test OutlineExplorerWidget
-# -------------------------------
-
-def test_outline_explorer(outline_explorer_bot):
+# ---- Test OutlineExplorerWidget
+def test_outline_explorer(outlineexplorer_bot):
     """
     Test to assert the outline explorer is initializing correctly and
     is showing the expected number of items, the expected type of items, and
     the expected text for each item.
     """
-    outline_explorer, qtbot = outline_explorer_bot
-    outline_explorer.show()
-    outline_explorer.setFixedSize(400, 350)
-    assert outline_explorer
+    outlineexplorer, qtbot = outlineexplorer_bot(
+        TEXT, 'test_outline_explorer.py')
+    assert outlineexplorer
 
-    outline_explorer.treewidget.expandAll()
-    tree_widget = outline_explorer.treewidget
+    outlineexplorer.treewidget.expandAll()
+    tree_widget = outlineexplorer.treewidget
     all_items = tree_widget.get_top_level_items() + tree_widget.get_items()
 
     # Assert that the expected number, text and type of items is displayed in
@@ -187,39 +187,34 @@ def test_outline_explorer(outline_explorer_bot):
             assert item.is_method() == expected_result[2]
 
 
-# Qt Test Fixtures
-# --------------------------------
-@pytest.fixture
-def outline_explorer_bot2(qtbot):
-    code_editor = CodeEditor()
-    code_editor = CodeEditor(None)
-    code_editor.set_language('py', 'test_file.py')
-    code_editor.set_text(dedent(code))
-
-    editor = OutlineExplorerProxyEditor(code_editor,
-                                        'test_file.py')
-
-    outline_explorer = OutlineExplorerWidget()
-    outline_explorer.set_current_editor(editor, False, False)
+def test_go_to_cursor_position(outlineexplorer_bot):
+    """
+    Test that clicking on the 'Go to cursor position' button located in the
+    toolbar of the outline explorer is working as expected.
+    """
+    outlineexplorer, qtbot = outlineexplorer_bot(TEXT, 'test.py')
 
-    qtbot.addWidget(outline_explorer)
+    # Move the mouse cursor in the editor to line 31 :
+    editor = outlineexplorer.treewidget.current_editor
+    editor._editor.go_to_line(31)
+    assert editor._editor.get_text_line(31) == "        if False:"
 
-    return outline_explorer, qtbot
+    # Click on the 'Go to cursor position' button of the outline explorer's
+    # toolbar :
+    qtbot.mouseClick(outlineexplorer.fromcursor_btn, Qt.LeftButton)
+    current_item = outlineexplorer.treewidget.currentItem()
+    assert current_item.text(0) == 'method1'
 
 
-# Test OutlineExplorerWidget
-# -------------------------------
-def test_code_cell_grouping(outline_explorer_bot2):
+def test_code_cell_grouping(outlineexplorer_bot):
     """
     Test to assert the outline explorer is initializing correctly and
     is showing the expected number of items, the expected type of items, and
     the expected text for each item. In addition this tests ancestry, code
     cells comments, code cell grouping and disabling this feature.
     """
-    outline_explorer, qtbot = outline_explorer_bot2
-    outline_explorer.show()
-    outline_explorer.setFixedSize(400, 350)
-    assert outline_explorer
+    outlineexplorer, qtbot = outlineexplorer_bot(dedent(CODE), 'test_file.py')
+    assert outlineexplorer
 
     expected_results = [
         ('test_file.py', FileRootItem),
@@ -251,8 +246,8 @@ def test_code_cell_grouping(outline_explorer_bot2):
         ('MGroup4', CellItem, 'MGroup3', 'test_file.py')
         ]
 
-    outline_explorer.treewidget.expandAll()
-    tree_widget = outline_explorer.treewidget
+    outlineexplorer.treewidget.expandAll()
+    tree_widget = outlineexplorer.treewidget
     cell_items = tree_widget.get_top_level_items() + tree_widget.get_items()
 
     # Assert that the expected number, text, ancestry and type of cell items is