Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Define Switcher Plugin public API + fix switcher position bug #20837

Merged
merged 19 commits into from
Apr 25, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions spyder/app/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,8 +477,8 @@ def main_window(request, tmpdir, qtbot):
for editorwindow in window.editor.editorwindows:
editorwindow.close()
editorstack = window.editor.get_current_editorstack()
if editorstack.switcher_dlg:
editorstack.switcher_dlg.close()
if editorstack.switcher_plugin:
editorstack.switcher_plugin.close()
AngelaRemolina marked this conversation as resolved.
Show resolved Hide resolved

window.projects.close_project()

Expand Down
22 changes: 10 additions & 12 deletions spyder/app/tests/test_mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2457,7 +2457,7 @@ def example_def_2():


@flaky(max_runs=3)
def test_edidorstack_open_switcher_dlg(main_window, tmpdir, qtbot):
def test_editorstack_open_switcher_dlg(main_window, tmpdir, qtbot):
"""
Test that the file switcher is working as expected when called from the
editorstack.
Expand All @@ -2472,16 +2472,15 @@ def test_edidorstack_open_switcher_dlg(main_window, tmpdir, qtbot):

# Add a file to the editor.
file = tmpdir.join('test_file_open_switcher_dlg.py')
file.write("a test file for test_edidorstack_open_switcher_dlg")
file.write("a test file for test_editorstack_open_switcher_dlg")
main_window.editor.load(str(file))

# Test that the file switcher opens as expected from the editorstack.
editorstack = main_window.editor.get_current_editorstack()
assert editorstack.switcher_dlg is None
editorstack.open_switcher_dlg()
assert editorstack.switcher_dlg
assert editorstack.switcher_dlg.isVisible()
assert (editorstack.switcher_dlg.count() ==
editorstack.switcher_plugin.get_container().open_switcher()
AngelaRemolina marked this conversation as resolved.
Show resolved Hide resolved
assert editorstack.switcher_plugin
assert editorstack.switcher_plugin.is_visible()
assert (editorstack.switcher_plugin.count() ==
len(main_window.editor.get_filenames()))


Expand Down Expand Up @@ -2527,11 +2526,10 @@ def example_def_2():

# Test that the symbol finder opens as expected from the editorstack.
editorstack = main_window.editor.get_current_editorstack()
assert editorstack.switcher_dlg is None
editorstack.open_symbolfinder_dlg()
assert editorstack.switcher_dlg
assert editorstack.switcher_dlg.isVisible()
assert editorstack.switcher_dlg.count() == 2
editorstack.switcher_plugin.get_container().open_switcher()
assert editorstack.switcher_plugin
assert editorstack.switcher_plugin.is_visible()
assert editorstack.switcher_plugin.count() == 2


@flaky(max_runs=3)
Expand Down
9 changes: 2 additions & 7 deletions spyder/plugins/editor/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
SelectionContextModificator, ExtraAction)
from spyder.plugins.editor.confpage import EditorConfigPage
from spyder.plugins.editor.utils.autosave import AutosaveForPlugin
from spyder.plugins.switcher.manager import EditorSwitcherManager
from spyder.plugins.editor.utils.switcher_manager import EditorSwitcherManager
from spyder.plugins.editor.widgets.codeeditor import CodeEditor
from spyder.plugins.editor.widgets.editor import (EditorMainWindow,
EditorSplitter,
Expand Down Expand Up @@ -1362,14 +1362,9 @@ def register_plugin(self):

self.add_dockwidget()

# Add modes to switcher
# TODO: 'Switcher' object has no attribute 'add_mode'
# it is needed to create a public API that contains the methods
# that handles the EditorSwitcherManager

self.switcher_manager = EditorSwitcherManager(
self,
self.main.switcher.get_container().switcher,
self.main.switcher,
self.get_current_editor,
self.get_current_editorstack,
section=self.get_plugin_title())
Expand Down
1 change: 1 addition & 0 deletions spyder/plugins/editor/tests/test_editor_config_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class MainWindowMock(QMainWindow):
ipyconsole = Mock()
mainmenu = Mock()
sig_setup_finished = Mock()
switcher = Mock()


@pytest.mark.parametrize(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class EditorSwitcherManager(object):
LINE_MODE = ':'
FILES_MODE = ''

def __init__(self, plugin, switcher_instance, get_codeeditor,
def __init__(self, plugin, switcher_plugin, get_codeeditor,
get_editorstack, section=_("Editor")):
"""
'get_codeeditor' and 'get_editorstack' params should be callables
Expand All @@ -42,7 +42,7 @@ def __init__(self, plugin, switcher_instance, get_codeeditor,
current_editorstack = get_editorstack()
"""
self._plugin = plugin
self._switcher = switcher_instance
self._switcher = switcher_plugin
self._editor = get_codeeditor
self._editorstack = get_editorstack
self._section = section
Expand Down Expand Up @@ -231,7 +231,7 @@ def line_switcher_handler(self, data, search_text, visible=False):
try:
line_number = int(line_number)
editorstack.go_to_line(line_number)
self._switcher.setVisible(visible)
self._switcher.set_visible(visible)
# Closing the switcher
if not visible:
self._current_line = None
Expand Down
67 changes: 15 additions & 52 deletions spyder/plugins/editor/widgets/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
get_filter, is_kde_desktop, is_anaconda)
from spyder.plugins.editor.utils.autosave import AutosaveForStack
from spyder.plugins.editor.utils.editor import get_file_language
from spyder.plugins.switcher.manager import EditorSwitcherManager
from spyder.plugins.editor.widgets import codeeditor
from spyder.plugins.editor.widgets.editorstack_helpers import (
ThreadManager, FileInfo, StackHistory)
Expand Down Expand Up @@ -280,7 +279,7 @@ class EditorStack(QWidget, SpyderConfigurationAccessor):
:py:meth:spyder.plugins.editor.widgets.editor.EditorStack.send_to_help
"""

def __init__(self, parent, actions):
def __init__(self, parent, actions, use_switcher=True):
QWidget.__init__(self, parent)

self.setAttribute(Qt.WA_DeleteOnClose)
Expand All @@ -297,10 +296,17 @@ def __init__(self, parent, actions):
self.setLayout(layout)

self.menu = None
self.switcher_dlg = None
self.switcher_manager = None
self.tabs = None
self.tabs_switcher = None
self.switcher_plugin = None
ccordoba12 marked this conversation as resolved.
Show resolved Hide resolved

switcher_action = None
symbolfinder_action = None
if use_switcher:
self.switcher_plugin = self.get_plugin().main.switcher
switcher_action = self.switcher_plugin.get_action("file switcher")
symbolfinder_action = self.switcher_plugin.get_action("symbol finder")

self.stack_history = StackHistory(self)

Expand All @@ -313,16 +319,6 @@ def __init__(self, parent, actions):

self.data = []

switcher_action = create_action(
self,
_("File switcher..."),
icon=ima.icon('filelist'),
triggered=self.open_switcher_dlg)
symbolfinder_action = create_action(
self,
_("Find symbols in file..."),
icon=ima.icon('symbol_find'),
triggered=self.open_symbolfinder_dlg)
copy_to_cb_action = create_action(self, _("Copy path to clipboard"),
icon=ima.icon('editcopy'),
triggered=lambda:
Expand Down Expand Up @@ -792,41 +788,6 @@ def clone_from(self, other):
self.clone_editor_from(other_finfo, set_current=True)
self.set_stack_index(other.get_stack_index())

@Slot()
@Slot(str)
def open_switcher_dlg(self, initial_text=''):
"""Open file list management dialog box"""
if not self.tabs.count():
return
if self.switcher_dlg is not None and self.switcher_dlg.isVisible():
self.switcher_dlg.hide()
self.switcher_dlg.clear()
return
if self.switcher_dlg is None:
from spyder.plugins.switcher.widgets.switcher import Switcher
self.switcher_dlg = Switcher(self)
self.switcher_manager = EditorSwitcherManager(
self.get_plugin(),
self.switcher_dlg,
self.get_current_editor,
lambda: self,
section=self.get_plugin_title())

if isinstance(initial_text, bool):
initial_text = ''

self.switcher_dlg.set_search_text(initial_text)
self.switcher_dlg.setup()
self.switcher_dlg.show()
# Note: the +1 pixel on the top makes it look better
delta_top = (self.tabs.tabBar().geometry().height() +
self.fname_label.geometry().height() + 1)
self.switcher_dlg.set_position(delta_top)

@Slot()
def open_symbolfinder_dlg(self):
self.open_switcher_dlg(initial_text='@')

def get_plugin(self):
"""Get the plugin of the parent widget."""
# Needed for the editor stack to use its own switcher instance.
Expand Down Expand Up @@ -3003,7 +2964,8 @@ class EditorSplitter(QSplitter):
"""QSplitter for editor windows."""

def __init__(self, parent, plugin, menu_actions, first=False,
register_editorstack_cb=None, unregister_editorstack_cb=None):
register_editorstack_cb=None, unregister_editorstack_cb=None,
use_switcher=True):
"""Create a splitter for dividing an editor window into panels.

Adds a new EditorStack instance to this splitter. If it's not
Expand Down Expand Up @@ -3039,7 +3001,7 @@ def __init__(self, parent, plugin, menu_actions, first=False,
self.unregister_editorstack_cb = unregister_editorstack_cb

self.menu_actions = menu_actions
self.editorstack = EditorStack(self, menu_actions)
self.editorstack = EditorStack(self, menu_actions, use_switcher)
self.register_editorstack_cb(self.editorstack)
if not first:
self.plugin.clone_editorstack(editorstack=self.editorstack)
Expand Down Expand Up @@ -3559,13 +3521,14 @@ def __init__(self):
self.editorstacks = []
self.editorwindows = []

self.last_focused_editorstack = {} # fake
self.last_focused_editorstack = {} # fake

self.find_widget = FindReplace(self, enable_replace=True)
self.outlineexplorer = OutlineExplorerWidget(None, self, self)
self.outlineexplorer.edit_goto.connect(self.go_to_file)
self.editor_splitter = EditorSplitter(self, self, menu_actions,
first=True)
first=True,
use_switcher=False)

editor_widgets = QWidget(self)
editor_layout = QVBoxLayout()
Expand Down
2 changes: 1 addition & 1 deletion spyder/plugins/editor/widgets/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def codeeditor_factory():


def editor_factory(new_file=True, text=None):
editorstack = EditorStack(None, [])
editorstack = EditorStack(None, [], False)
editorstack.set_find_widget(FindReplace(editorstack))
editorstack.set_io_actions(Mock(), Mock(), Mock(), Mock())
if new_file:
Expand Down
2 changes: 1 addition & 1 deletion spyder/plugins/editor/widgets/tests/test_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
# =============================================================================
@pytest.fixture
def base_editor_bot(qtbot):
editor_stack = EditorStack(None, [])
editor_stack = EditorStack(None, [], False)
editor_stack.set_find_widget(Mock())
editor_stack.set_io_actions(Mock(), Mock(), Mock(), Mock())
return editor_stack
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def get_tree_elements(treewidget):
this_tree = {node.name: []}
parent_tree.append(this_tree)
this_stack = [(this_tree[node.name], child)
for child in node.children]
for child in node.children]
stack = this_stack + stack
return root_tree

Expand Down Expand Up @@ -119,7 +119,7 @@ def completions_codeeditor_outline(completions_codeeditor, outlineexplorer):
@pytest.fixture
def editorstack(qtbot, outlineexplorer):
def _create_editorstack(files):
editorstack = editor.EditorStack(None, [])
editorstack = editor.EditorStack(None, [], False)
editorstack.set_find_widget(Mock())
editorstack.set_io_actions(Mock(), Mock(), Mock(), Mock())
editorstack.analysis_timer = Mock()
Expand Down
2 changes: 1 addition & 1 deletion spyder/plugins/editor/widgets/tests/test_editorsplitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
# ---- Qt Test Fixtures

def editor_stack():
editor_stack = EditorStack(None, [])
editor_stack = EditorStack(None, [], False)
editor_stack.set_find_widget(Mock())
editor_stack.set_io_actions(Mock(), Mock(), Mock(), Mock())
return editor_stack
Expand Down
2 changes: 1 addition & 1 deletion spyder/plugins/editor/widgets/tests/test_save.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def add_files(editorstack):
# ---- Qt Test Fixtures
@pytest.fixture
def base_editor_bot(qtbot):
editor_stack = editor.EditorStack(None, [])
editor_stack = editor.EditorStack(None, [], False)
editor_stack.set_find_widget(Mock())
editor_stack.set_io_actions(Mock(), Mock(), Mock(), Mock())
return editor_stack, qtbot
Expand Down
2 changes: 1 addition & 1 deletion spyder/plugins/editor/widgets/tests/test_shortcuts.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def editor_bot(qtbot):
Set up EditorStack with CodeEditors containing some Python code.
The cursor is at the empty line below the code.
"""
editorstack = EditorStack(None, [])
editorstack = EditorStack(None, [], False)
editorstack.set_find_widget(Mock())
editorstack.set_io_actions(Mock(), Mock(), Mock(), Mock())
editorstack.close_action.setEnabled(False)
Expand Down
48 changes: 17 additions & 31 deletions spyder/plugins/switcher/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,16 @@

# Third-party imports
from qtpy.QtCore import Qt
from qtpy.QtCore import Signal
from qtpy.QtWidgets import QApplication

# Spyder imports
from spyder.api.config.decorators import on_conf_change
from spyder.api.translations import _
from spyder.api.widgets.main_container import PluginMainContainer
from spyder.plugins.switcher.widgets.switcher import Switcher


class SwitcherContainer(PluginMainContainer):

# Signals
# signals needed when the EditorSwitcherManager is migrated
# Dismissed switcher
sig_rejected = Signal()
# Search/Filter text changes
sig_text_changed = Signal()
# Current item changed
sig_item_changed = Signal()
# List item selected, mode and cleaned search text
sig_item_selected = Signal()
sig_mode_selected = Signal()

# --- PluginMainContainer API
# ------------------------------------------------------------------------
def setup(self):
Expand All @@ -47,7 +34,7 @@ def setup(self):
register_shortcut=True,
context=Qt.ApplicationShortcut
)

self.create_action(
'symbol finder',
_('Symbol finder...'),
Expand All @@ -58,13 +45,6 @@ def setup(self):
context=Qt.ApplicationShortcut
)

# Signals
self.switcher.sig_rejected.connect(self.sig_rejected)
self.switcher.sig_text_changed.connect(self.sig_text_changed)
self.switcher.sig_item_changed.connect(self.sig_item_changed)
self.switcher.sig_item_selected.connect(self.sig_item_selected)
self.switcher.sig_mode_selected.connect(self.sig_mode_selected)

def update_actions(self):
pass

Expand All @@ -84,18 +64,24 @@ def open_switcher(self, symbol=False):
switcher.set_search_text('')
switcher.setup()

switcher.show()
# Set position
mainwindow = self._plugin.get_main()

# Note: The +8 pixel on the top makes it look better
AngelaRemolina marked this conversation as resolved.
Show resolved Hide resolved
# FIXME: Why is this using the toolbars menu? A: To not be on top of
# the toolbars.
# Probably toolbars should be taken into account for this 'delta' only
# when are visible
mainwindow = self._plugin.get_main()
delta_top = (mainwindow.toolbar.toolbars_menu.geometry().height() +
mainwindow.menuBar().geometry().height() + 8)
default_top = (mainwindow.toolbar.toolbars_menu.geometry().height() +
mainwindow.menuBar().geometry().height() + 8)

current_window = QApplication.activeWindow()
if current_window == mainwindow:
if self.get_conf('toolbars_visible', section='toolbar'):
delta_top = default_top
else:
delta_top = mainwindow.menuBar().geometry().height() + 8
else:
delta_top = default_top

switcher.set_position(delta_top)
switcher.set_position(delta_top, current_window)
switcher.show()

def open_symbolfinder(self):
"""Open symbol list management dialog box."""
Expand Down
Loading