From d65e5f1cadcd1454d4a8c75f7866e460bccbe75d Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Sun, 19 Feb 2023 08:36:04 +0100 Subject: [PATCH 01/19] Transition Debugger plugin to run plugin and cleanup editor Plugin --- spyder/app/tests/test_mainwindow.py | 19 +- spyder/config/main.py | 8 +- spyder/plugins/console/widgets/main_widget.py | 5 +- spyder/plugins/debugger/plugin.py | 250 ++++++++++++------ .../plugins/debugger/widgets/framesbrowser.py | 6 +- .../plugins/debugger/widgets/main_widget.py | 39 ++- spyder/plugins/editor/confpage.py | 19 -- spyder/plugins/editor/plugin.py | 50 +--- spyder/plugins/editor/widgets/codeeditor.py | 6 - spyder/plugins/editor/widgets/editor.py | 55 ---- spyder/plugins/ipythonconsole/api.py | 4 +- spyder/plugins/ipythonconsole/plugin.py | 59 ++--- .../ipythonconsole/widgets/debugging.py | 7 +- .../ipythonconsole/widgets/main_widget.py | 53 +--- spyder/plugins/run/api.py | 4 + spyder/plugins/run/confpage.py | 15 ++ spyder/plugins/run/container.py | 2 +- spyder/plugins/run/plugin.py | 14 + 18 files changed, 292 insertions(+), 323 deletions(-) diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index 55606b62dca..3f750cbfe2b 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -1630,7 +1630,7 @@ def test_run_code(main_window, qtbot, tmpdir): @flaky(max_runs=3) @pytest.mark.skipif(sys.platform == 'darwin', reason="It fails on macOS") @pytest.mark.parametrize('main_window', - [{'spy_config': ('editor', 'run_cell_copy', True)}], + [{'spy_config': ('run', 'run_cell_copy', True)}], indirect=True) @pytest.mark.order(after="test_debug_unsaved_function") def test_run_cell_copy(main_window, qtbot, tmpdir): @@ -1646,9 +1646,7 @@ def test_run_cell_copy(main_window, qtbot, tmpdir): qtbot.waitUntil( lambda: shell.spyder_kernel_ready and shell._prompt_html is not None, timeout=SHELL_TIMEOUT) - # Make sure run_cell_copy is properly set - for editorstack in main_window.editor.editorstacks: - editorstack.set_run_cell_copy(True) + # Load test file main_window.editor.load(filepath) @@ -1685,7 +1683,7 @@ def test_run_cell_copy(main_window, qtbot, tmpdir): # ---- Closing test file and reset config ---- main_window.editor.close_file() - CONF.set('editor', 'run_cell_copy', False) + CONF.set('run', 'run_cell_copy', False) @flaky(max_runs=3) @@ -1821,8 +1819,8 @@ def get_random_plugin(): # editor by error debugger = main_window.debugger debug_next_action = debugger.get_action(DebuggerWidgetActions.Next) - debug_next_button = debugger.get_widget()._main_toolbar.widgetForAction( - debug_next_action) + debug_next_button = debugger.get_widget()._auxiliary_toolbars[ + "widget_control"].widgetForAction(debug_next_action) with qtbot.waitSignal(shell.executed): qtbot.mouseClick(debug_next_button, Qt.LeftButton) assert not main_window.editor._ismaximized @@ -3048,7 +3046,7 @@ def test_preferences_change_font_regression(main_window, qtbot): @pytest.mark.skipif(running_in_ci(), reason="Fails on CIs") @pytest.mark.parametrize('main_window', - [{'spy_config': ('editor', 'run_cell_copy', True)}], + [{'spy_config': ('run', 'run_cell_copy', True)}], indirect=True) def test_preferences_empty_shortcut_regression(main_window, qtbot): """ @@ -3524,7 +3522,7 @@ def test_varexp_refresh(main_window, qtbot): @pytest.mark.skipif(sys.platform == 'darwin' or os.name == 'nt', reason="Fails on macOS and Windows") @pytest.mark.parametrize('main_window', - [{'spy_config': ('editor', 'run_cell_copy', False)}], + [{'spy_config': ('run', 'run_cell_copy', False)}], indirect=True) @pytest.mark.order(after="test_debug_unsaved_function") def test_runcell_edge_cases(main_window, qtbot, tmpdir): @@ -5431,7 +5429,8 @@ def test_out_runfile_runcell(main_window, qtbot): CONF.set('run', 'last_used_parameters', run_parameters) with qtbot.waitSignal(shell.executed): - main_window.editor.run_cell() + qtbot.mouseClick(main_window.run_cell_button, + Qt.LeftButton) if shown: assert "]: " + str(num) in control.toPlainText() else: diff --git a/spyder/config/main.py b/spyder/config/main.py index c827e01f15a..a92ccfd058a 100644 --- a/spyder/config/main.py +++ b/spyder/config/main.py @@ -194,6 +194,11 @@ 'pdb_stop_first_line': True, 'breakpoints_panel': True, }), + ('run', + { + 'save_all_before_run': True, + 'run_cell_copy': False, + }), ('plots', { 'mute_inline_plotting': True, @@ -247,9 +252,6 @@ 'show_tab_bar': True, 'show_class_func_dropdown': False, 'max_recent_files': 20, - 'save_all_before_run': True, - 'focus_to_editor': True, - 'run_cell_copy': False, 'onsave_analysis': False, 'autosave_enabled': True, 'autosave_interval': 60, diff --git a/spyder/plugins/console/widgets/main_widget.py b/spyder/plugins/console/widgets/main_widget.py index 33be9c4f126..00b3be8e1e3 100644 --- a/spyder/plugins/console/widgets/main_widget.py +++ b/spyder/plugins/console/widgets/main_widget.py @@ -496,7 +496,7 @@ def show_syspath(self): self.dialog_manager.show(editor) @Slot() - def run_script(self, filename=None, silent=False, set_focus=False, + def run_script(self, filename=None, silent=False, args=None): """ Run a Python script. @@ -522,9 +522,6 @@ def run_script(self, filename=None, silent=False, set_focus=False, rbs = remove_backslashes command = "runfile('%s', args='%s')" % (rbs(filename), rbs(args)) - if set_focus: - self.shell.setFocus() - self.change_visibility(True, True) self.shell.write(command+'\n') diff --git a/spyder/plugins/debugger/plugin.py b/spyder/plugins/debugger/plugin.py index a3b88746881..ace00ecdf94 100644 --- a/spyder/plugins/debugger/plugin.py +++ b/spyder/plugins/debugger/plugin.py @@ -8,6 +8,7 @@ # Standard library imports import os.path as osp +from typing import List # Third-party imports from qtpy.QtCore import Slot @@ -30,20 +31,23 @@ from spyder.plugins.ipythonconsole.api import IPythonConsolePyConfiguration from spyder.plugins.mainmenu.api import ApplicationMenus, DebugMenuSections from spyder.plugins.run.api import ( - WorkingDirOpts, WorkingDirSource, RunExecutionParameters, - ExtendedRunExecutionParameters) + WorkingDirOpts, WorkingDirSource, RunExecutionParameters, RunConfiguration, + ExtendedRunExecutionParameters, RunExecutor, run_execute, RunContext, + RunResult) from spyder.plugins.toolbar.api import ApplicationToolbars +from spyder.plugins.ipythonconsole.widgets.config import IPythonConfigOptions +from spyder.plugins.editor.api.run import CellRun, SelectionRun # Localization _ = get_translation("spyder") -class Debugger(SpyderDockablePlugin, ShellConnectMixin): +class Debugger(SpyderDockablePlugin, ShellConnectMixin, RunExecutor): """Debugger plugin.""" NAME = 'debugger' - REQUIRES = [Plugins.IPythonConsole, Plugins.Preferences] + REQUIRES = [Plugins.IPythonConsole, Plugins.Preferences, Plugins.Run] OPTIONAL = [Plugins.Editor, Plugins.MainMenu, Plugins.Toolbar, Plugins.VariableExplorer] TABIFY = [Plugins.VariableExplorer, Plugins.Help] @@ -63,7 +67,7 @@ def get_description(self): return _('Display and explore frames while debugging.') def get_icon(self): - return self.create_icon('dictedit') + return self.create_icon('debug') def on_initialize(self): widget = self.get_widget() @@ -80,6 +84,66 @@ def on_initialize(self): widget.sig_load_pdb_file.connect( self._load_pdb_file_in_editor) + self.python_editor_run_configuration = { + 'origin': self.NAME, + 'extension': 'py', + 'contexts': [ + { + 'name': 'File' + }, + { + 'name': 'Cell' + }, + { + 'name': 'Selection' + }, + ] + } + + self.executor_configuration = [ + { + 'input_extension': 'py', + 'context': { + 'name': 'File' + }, + 'output_formats': [], + 'configuration_widget': IPythonConfigOptions, + 'requires_cwd': True, + 'priority': 10 + }, + { + 'input_extension': 'py', + 'context': { + 'name': 'Cell' + }, + 'output_formats': [], + 'configuration_widget': None, + 'requires_cwd': True, + 'priority': 10 + }, + { + 'input_extension': 'py', + 'context': { + 'name': 'Selection' + }, + 'output_formats': [], + 'configuration_widget': None, + 'requires_cwd': True, + 'priority': 10 + }, + ] + + @on_plugin_available(plugin=Plugins.Run) + def on_run_available(self): + run = self.get_plugin(Plugins.Run) + run.register_executor_configuration(self, self.executor_configuration) + + @on_plugin_teardown(plugin=Plugins.Run) + def on_run_teardown(self): + run = self.get_plugin(Plugins.Run) + run.deregister_executor_configuration( + self, self.executor_configuration) + @on_plugin_available(plugin=Plugins.Preferences) def on_preferences_available(self): preferences = self.get_plugin(Plugins.Preferences) @@ -95,6 +159,9 @@ def on_editor_available(self): editor = self.get_plugin(Plugins.Editor) widget = self.get_widget() + editor.add_supported_run_configuration( + self.python_editor_run_configuration) + # The editor is available, connect signals. widget.sig_edit_goto.connect(editor.load) editor.sig_codeeditor_created.connect(self._add_codeeditor) @@ -124,6 +191,9 @@ def on_editor_teardown(self): editor = self.get_plugin(Plugins.Editor) widget = self.get_widget() + editor.remove_supported_run_configuration( + self.python_editor_run_configuration) + widget.sig_edit_goto.disconnect(editor.load) editor.sig_codeeditor_created.disconnect(self._add_codeeditor) @@ -390,83 +460,6 @@ def _set_or_edit_conditional_breakpoint(self): # ---- Public API # ------------------------------------------------------------------------ - @Slot() - def debug_file(self): - """ - Debug current file. - - It should only be called when an editor is available. - """ - editor = self.get_plugin(Plugins.Editor, error=False) - if not editor: - return - - editor.switch_to_plugin() - - # TODO: This is a temporary measure to debug files whilst the debug API - # is defined. - current_fname = editor.get_current_filename() - fname_uuid = editor.id_per_file[current_fname] - run_conf = editor.get_run_configuration(fname_uuid) - fname_params = self.main.run.get_last_used_executor_parameters( - fname_uuid) - - selected = None - if fname_params['executor'] == self.main.ipyconsole.NAME: - selected = fname_params['selected'] - - if selected is None: - ipy_params = IPythonConsolePyConfiguration( - current=True, post_mortem=False, - python_args_enabled=False, python_args='', - clear_namespace=False, console_namespace=False) - wdir_opts = WorkingDirOpts( - source=WorkingDirSource.CurrentDirectory, - path=osp.dirname(current_fname)) - - exec_conf = RunExecutionParameters( - working_dir=wdir_opts, executor_params=ipy_params) - ext_exec_conf = ExtendedRunExecutionParameters( - uuid=None, name=None, params=exec_conf) - else: - run_plugin = self.main.run - all_exec_conf = run_plugin.get_executor_configuration_parameters( - self.main.ipyconsole.NAME, 'py', RunContext.File - ) - all_exec_conf = all_exec_conf['params'] - ext_exec_conf = all_exec_conf[selected] - - ext_exec_conf['params']['executor_params']['debug'] = True - self.main.run.run_configuration( - self.main.ipyconsole.NAME, run_conf, ext_exec_conf) - - @Slot() - def debug_cell(self): - """ - Debug current cell. - - It should only be called when an editor is available. - """ - # TODO: This is a temporary measure to debug selections whilst the - # debug API is defined. - editor = self.get_plugin(Plugins.Editor, error=False) - if editor: - editor.run_cell(method="debugcell") - - @Slot() - def debug_selection(self): - """ - Debug current selection or line. - - It should only be called when an editor is available. - """ - # TODO: This is a temporary measure to debug selections whilst the - # debug API is defined. - editor = self.get_plugin(Plugins.Editor, error=False) - if editor: - editorstack = editor.get_current_editorstack() - text = "%%debug\n" + editorstack.run_selection()[0] - self.main.ipyconsole.run_selection(text) @Slot() def clear_all_breakpoints(self): @@ -516,3 +509,96 @@ def can_close_file(self, filename=None): widget.print_debug_file_msg() return False + + # ---- For execution + @run_execute(context=RunContext.File) + def exec_files( + self, + input: RunConfiguration, + conf: ExtendedRunExecutionParameters + ) -> List[RunResult]: + + console = self.get_plugin(Plugins.IPythonConsole) + if console is None: + return + + exec_params = conf['params'] + params: IPythonConsolePyConfiguration = exec_params['executor_params'] + params["run_method"] = "debugfile" + + console.exec_files(input, conf) + + self.get_widget().set_pdb_take_focus(False) + + @run_execute(context=RunContext.Cell) + def exec_cell( + self, + input: RunConfiguration, + conf: ExtendedRunExecutionParameters + ) -> List[RunResult]: + + console = self.get_plugin(Plugins.IPythonConsole) + if console is None: + return + + run_input: CellRun = input['run_input'] + if run_input['copy']: + console.run_selection("%%debug\n" + run_input['cell']) + return + + exec_params = conf['params'] + params: IPythonConsolePyConfiguration = exec_params['executor_params'] + params["run_method"] = "debugcell" + + console.exec_cell(input, conf) + + self.get_widget().set_pdb_take_focus(False) + + + @run_execute(context=RunContext.Selection) + def exec_selection( + self, + input: RunConfiguration, + conf: ExtendedRunExecutionParameters + ) -> List[RunResult]: + + console = self.get_plugin(Plugins.IPythonConsole) + if console is None: + return + + run_input: SelectionRun = input['run_input'] + run_input['selection'] = "%%debug\n" + run_input['selection'] + + console.exec_selection(input, conf) + + self.get_widget().set_pdb_take_focus(False) + + @Slot() + def debug_file(self): + """ + Debug current file. + """ + run = self.get_plugin(Plugins.Run) + if run is None: + return + run.run_current_configuration(self.NAME, RunContext.File) + + @Slot() + def debug_cell(self): + """ + Debug current cell. + """ + run = self.get_plugin(Plugins.Run) + if run is None: + return + run.run_current_configuration(self.NAME, RunContext.Cell) + + @Slot() + def debug_selection(self): + """ + Debug current selection or line. + """ + run = self.get_plugin(Plugins.Run) + if run is None: + return + run.run_current_configuration(self.NAME, RunContext.Selection) diff --git a/spyder/plugins/debugger/widgets/framesbrowser.py b/spyder/plugins/debugger/widgets/framesbrowser.py index 76b12b0b2e3..0350ed21ef4 100644 --- a/spyder/plugins/debugger/widgets/framesbrowser.py +++ b/spyder/plugins/debugger/widgets/framesbrowser.py @@ -106,9 +106,9 @@ def pdb_has_stopped(self, fname, lineno): """Handle pdb has stopped""" # this will set the focus to the editor self.sig_load_pdb_file.emit(fname, lineno) - if self.shellwidget._pdb_focus_to_editor: - # Focus to editor will be requested each time - self.shellwidget._pdb_focus_to_editor = False + if self.shellwidget._pdb_take_focus: + # Not taking focus will be required on each call to the debugger + self.shellwidget._pdb_take_focus = True else: # take back focus self.shellwidget._control.setFocus() diff --git a/spyder/plugins/debugger/widgets/main_widget.py b/spyder/plugins/debugger/widgets/main_widget.py index 59d75c1e2a5..75c180b5f78 100644 --- a/spyder/plugins/debugger/widgets/main_widget.py +++ b/spyder/plugins/debugger/widgets/main_widget.py @@ -273,7 +273,7 @@ def setup(self): register_shortcut=True ) - self.create_action( + debug_file_action = self.create_action( DebuggerToolbarActions.DebugCurrentFile, text=_("&Debug file"), tip=_("Debug file"), @@ -282,7 +282,7 @@ def setup(self): register_shortcut=True, ) - self.create_action( + debug_cell_action = self.create_action( DebuggerToolbarActions.DebugCurrentCell, text=_("Debug cell"), tip=_("Debug cell"), @@ -291,7 +291,7 @@ def setup(self): register_shortcut=True, ) - self.create_action( + debug_selection_action = self.create_action( DebuggerToolbarActions.DebugCurrentSelection, text=_("Debug selection or current line"), tip=_("Debug selection or current line"), @@ -347,18 +347,31 @@ def setup(self): # Main toolbar main_toolbar = self.get_main_toolbar() + secondary_toolbar = self.create_toolbar("widget_control") + + for item in [ + debug_file_action, + debug_cell_action, + debug_selection_action, + enter_debug_action, + inspect_action, + ]: + self.add_item_to_toolbar( + item, + toolbar=main_toolbar, + section=DebuggerWidgetMainToolBarSections.Main, + ) + for item in [next_action, continue_action, step_action, return_action, stop_action, goto_cursor_action, - enter_debug_action, - inspect_action, search_action]: self.add_item_to_toolbar( item, - toolbar=main_toolbar, + toolbar=secondary_toolbar, section=DebuggerWidgetMainToolBarSections.Main, ) @@ -528,6 +541,16 @@ def print_debug_file_msg(self): 'in debug mode.') sw.append_html_message(debug_msg, before_prompt=True) + def set_pdb_take_focus(self, take_focus): + """ + Set whether current pdb should take focus when stopping on the + next call. + """ + widget = self.current_widget() + if widget is None: + return False + widget.shellwidget._pdb_take_focus = take_focus + @Slot(bool) def toggle_finder(self, checked): """Show or hide finder.""" @@ -622,6 +645,4 @@ def debug_command(self, command): widget = self.current_widget() if widget is None: return - focus_to_editor = self.get_conf("focus_to_editor", section="editor") - widget.shellwidget.pdb_execute_command( - command, focus_to_editor=focus_to_editor) + widget.shellwidget.pdb_execute_command(command) diff --git a/spyder/plugins/editor/confpage.py b/spyder/plugins/editor/confpage.py index cf2d4fcd73a..7464cff5cbe 100644 --- a/spyder/plugins/editor/confpage.py +++ b/spyder/plugins/editor/confpage.py @@ -205,24 +205,6 @@ def enable_tabwidth_spin(index): sourcecode_widget = QWidget() sourcecode_widget.setLayout(sourcecode_layout) - # --- Run code tab --- - saveall_box = newcb(_("Save all files before running script"), - 'save_all_before_run') - focus_box = newcb( - _("Maintain focus in the editor after running or debugging code"), - 'focus_to_editor' - ) - run_cell_box = newcb(_("Copy full cell contents to the console when " - "running code cells"), 'run_cell_copy') - - run_layout = QVBoxLayout() - run_layout.addWidget(saveall_box) - run_layout.addWidget(focus_box) - run_layout.addWidget(run_cell_box) - - run_widget = QWidget() - run_widget.setLayout(run_layout) - # --- Advanced tab --- # -- Templates template_btn = self.create_button(_("Edit template for new files"), @@ -334,7 +316,6 @@ def enable_tabwidth_spin(index): self.tabs = QTabWidget() self.tabs.addTab(self.create_tab(display_widget), _("Display")) self.tabs.addTab(self.create_tab(sourcecode_widget), _("Source code")) - self.tabs.addTab(self.create_tab(run_widget), _('Run code')) self.tabs.addTab(self.create_tab(template_btn, autosave_group, docstring_group, annotations_group, eol_group), diff --git a/spyder/plugins/editor/plugin.py b/spyder/plugins/editor/plugin.py index 843a8375a2f..95bc9cc3bf7 100644 --- a/spyder/plugins/editor/plugin.py +++ b/spyder/plugins/editor/plugin.py @@ -72,10 +72,6 @@ logger = logging.getLogger(__name__) -RunContext.File = 'file' -RunContext.Selection = 'selection' -RunContext.Cell = 'cell' - class Editor(SpyderPluginWidget, SpyderConfigurationObserver): """ @@ -94,11 +90,6 @@ class Editor(SpyderPluginWidget, SpyderConfigurationObserver): OPTIONAL = [Plugins.Completions, Plugins.OutlineExplorer] # Signals - sig_run_file_in_ipyclient = Signal( - str, str, str, bool, bool, bool, bool, bool, str, bool) - sig_run_cell_in_ipyclient = Signal(str, object, str, bool, str, bool) - - exec_in_extconsole = Signal(str, bool) redirect_stdio = Signal(bool) sig_dir_opened = Signal(str) @@ -1443,8 +1434,6 @@ def register_editorstack(self, editorstack): ('set_edgeline_enabled', 'edge_line'), ('set_indent_guides', 'indent_guides'), ('set_code_folding_enabled', 'code_folding'), - ('set_focus_to_editor', 'focus_to_editor'), - ('set_run_cell_copy', 'run_cell_copy'), ('set_close_parentheses_enabled', 'close_parentheses'), ('set_close_quotes_enabled', 'close_quotes'), ('set_add_colons_enabled', 'add_colons'), @@ -1513,11 +1502,6 @@ def register_editorstack(self, editorstack): editorstack.sig_option_changed.connect(self.sig_option_changed) editorstack.redirect_stdio.connect( lambda state: self.redirect_stdio.emit(state)) - editorstack.exec_in_extconsole.connect( - lambda text, option: - self.exec_in_extconsole.emit(text, option)) - editorstack.sig_run_cell_in_ipyclient.connect( - self.sig_run_cell_in_ipyclient) editorstack.update_plugin_title.connect(self.sig_update_plugin_title) editorstack.editor_focus_changed.connect(self.save_focused_editorstack) editorstack.editor_focus_changed.connect(self.main.plugin_focus_changed) @@ -2832,7 +2816,7 @@ def handle_get_file_code(self, filename, save_all=True): Bytes are returned instead of str to support non utf-8 files. """ editorstack = self._get_editorstack() - if save_all and self.get_option('save_all_before_run'): + if save_all and self.get_option('save_all_before_run', section="run"): editorstack.save_all(save_new_files=False) editor = self._get_editor(filename) @@ -2939,7 +2923,7 @@ def remove_supported_run_configuration( def get_run_configuration(self, metadata_id: str) -> RunConfiguration: editorstack = self.get_current_editorstack() self.focus_run_configuration(metadata_id) - if self.get_option('save_all_before_run'): + if self.get_option('save_all_before_run', section="run"): editorstack.save_all(save_new_files=False) metadata = self.metadata_per_id[metadata_id] context = metadata['context']['name'] @@ -2956,7 +2940,7 @@ def get_run_configuration_per_context( re_run=False ) -> Optional[RunConfiguration]: editorstack = self.get_current_editorstack() - if self.get_option('save_all_before_run'): + if self.get_option('save_all_before_run', section="run"): editorstack.save_all(save_new_files=False) fname = self.get_current_filename() @@ -2987,7 +2971,7 @@ def get_run_configuration_per_context( info = editorstack.get_cell() text, offsets, line_cols, cell_name, enc = info context_name = 'Cell' - copy_cell = editorstack.run_cell_copy + copy_cell = self.get_option('run_cell_copy', section='run') run_input = CellRun( path=fname, cell=text, cell_name=cell_name, encoding=enc, line_col_bounds=line_cols, character_bounds=offsets, @@ -3037,24 +3021,6 @@ def run_from_line(self): editorstack = self.get_current_editorstack() editorstack.run_from_line() - @Slot() - def run_cell(self, method=None): - """Run current cell""" - editorstack = self.get_current_editorstack() - editorstack.run_cell(method=method) - - @Slot() - def run_cell_and_advance(self, method=None): - """Run current cell and advance to the next one""" - editorstack = self.get_current_editorstack() - editorstack.run_cell_and_advance(method) - - @Slot() - def re_run_last_cell(self): - """Run last executed cell.""" - editorstack = self.get_current_editorstack() - editorstack.re_run_last_cell() - # ------ Code bookmarks @Slot(int) def save_bookmark(self, slot_num): @@ -3123,8 +3089,6 @@ def apply_plugin_settings(self, options): occurrence_o = self.get_option(occurrence_n) occurrence_timeout_n = 'occurrence_highlighting/timeout' occurrence_timeout_o = self.get_option(occurrence_timeout_n) - focus_to_editor_n = 'focus_to_editor' - focus_to_editor_o = self.get_option(focus_to_editor_n) for editorstack in self.editorstacks: if currentline_n in options: @@ -3138,8 +3102,6 @@ def apply_plugin_settings(self, options): if occurrence_timeout_n in options: editorstack.set_occurrence_highlighting_timeout( occurrence_timeout_o) - if focus_to_editor_n in options: - editorstack.set_focus_to_editor(focus_to_editor_o) # --- everything else tabbar_n = 'show_tab_bar' @@ -3174,8 +3136,6 @@ def apply_plugin_settings(self, options): converteol_o = self.get_option(converteol_n) converteolto_n = 'convert_eol_on_save_to' converteolto_o = self.get_option(converteolto_n) - runcellcopy_n = 'run_cell_copy' - runcellcopy_o = self.get_option(runcellcopy_n) closepar_n = 'close_parentheses' closepar_o = self.get_option(closepar_n) close_quotes_n = 'close_quotes' @@ -3232,8 +3192,6 @@ def apply_plugin_settings(self, options): editorstack.set_convert_eol_on_save(converteol_o) if converteolto_n in options: editorstack.set_convert_eol_on_save_to(converteolto_o) - if runcellcopy_n in options: - editorstack.set_run_cell_copy(runcellcopy_o) if closepar_n in options: editorstack.set_close_parentheses_enabled(closepar_o) if close_quotes_n in options: diff --git a/spyder/plugins/editor/widgets/codeeditor.py b/spyder/plugins/editor/widgets/codeeditor.py index ecf96a24a89..a488d020e9b 100644 --- a/spyder/plugins/editor/widgets/codeeditor.py +++ b/spyder/plugins/editor/widgets/codeeditor.py @@ -176,12 +176,6 @@ class CodeEditor(TextEditBaseWidget): sig_bookmarks_changed = Signal() go_to_definition = Signal(str, int, int) sig_show_object_info = Signal(bool) - sig_run_selection = Signal() - sig_run_to_line = Signal() - sig_run_from_line = Signal() - sig_run_cell_and_advance = Signal() - sig_run_cell = Signal() - sig_re_run_last_cell = Signal() sig_cursor_position_changed = Signal(int, int) sig_new_file = Signal(str) sig_refresh_formatting = Signal(bool) diff --git a/spyder/plugins/editor/widgets/editor.py b/spyder/plugins/editor/widgets/editor.py index afdb195d8b5..f6ef870cfd2 100644 --- a/spyder/plugins/editor/widgets/editor.py +++ b/spyder/plugins/editor/widgets/editor.py @@ -179,8 +179,6 @@ class EditorStack(QWidget): starting_long_process = Signal(str) ending_long_process = Signal(str) redirect_stdio = Signal(bool) - exec_in_extconsole = Signal(str, bool) - sig_run_cell_in_ipyclient = Signal(str, object, str, bool, str, bool) update_plugin_title = Signal() editor_focus_changed = Signal() zoom_in = Signal() @@ -391,8 +389,6 @@ def __init__(self, parent, actions): self.remove_trailing_newlines = False self.convert_eol_on_save = False self.convert_eol_on_save_to = 'LF' - self.focus_to_editor = True - self.run_cell_copy = False self.create_new_file_if_empty = True self.indent_guides = False self.__file_status_flag = False @@ -1190,13 +1186,6 @@ def set_convert_eol_on_save_to(self, state): # CONF.get(self.CONF_SECTION, 'convert_eol_on_save_to') self.convert_eol_on_save_to = state - def set_focus_to_editor(self, state): - self.focus_to_editor = state - - def set_run_cell_copy(self, state): - """If `state` is ``True``, code cells will be copied to the console.""" - self.run_cell_copy = state - def set_current_project_path(self, root_path=None): """ Set the current active project root path. @@ -2857,8 +2846,6 @@ def run_selection(self): if editor.is_cursor_on_last_line() and text: editor.append(editor.get_line_separator()) - if self.focus_to_editor: - editor.move_cursor_to_next('line', 'down') return ( text, (line_off_from, line_off_to), @@ -2874,22 +2861,6 @@ def get_cell(self): name = cell_name(block) return text, off_pos, line_col_pos, name, encoding - def run_cell(self, method=None): - """Run current cell.""" - text, block, *__ = ( - self.get_current_editor().get_cell_as_executable_code()) - finfo = self.get_current_finfo() - editor = self.get_current_editor() - name = cell_name(block) - filename = finfo.filename - - self._run_cell_text(text, editor, (filename, name), method) - - def run_cell_and_advance(self, method=None): - """Run current cell and advance to the next one""" - self.run_cell(method) - self.advance_cell() - def advance_cell(self, reverse=False): """Advance to the next cell. @@ -2921,32 +2892,6 @@ def re_run_last_cell(self): return text, off_pos, col_pos, cell_name, encoding - def _run_cell_text(self, text, editor, cell_id, method=None): - """Run cell code in the console. - - Cell code is run in the console by copying it to the console if - `self.run_cell_copy` is ``True`` otherwise by using the `run_cell` - function. - - Parameters - ---------- - text : str - The code in the cell as a string. - line : int - The starting line number of the cell in the file. - """ - (filename, cell_name) = cell_id - if editor.is_python_or_ipython(): - if method is None: - method = "runcell" - # self.run_cell_copy only works for runcell - run_cell_copy = self.run_cell_copy - if method != "runcell": - run_cell_copy = False - self.sig_run_cell_in_ipyclient.emit( - text, cell_name, filename, run_cell_copy, method, - self.focus_to_editor) - # ------ Drag and drop def dragEnterEvent(self, event): """ diff --git a/spyder/plugins/ipythonconsole/api.py b/spyder/plugins/ipythonconsole/api.py index d924649536f..c19db1c61dd 100644 --- a/spyder/plugins/ipythonconsole/api.py +++ b/spyder/plugins/ipythonconsole/api.py @@ -38,5 +38,5 @@ class IPythonConsolePyConfiguration(TypedDict): # then it will use an empty one. console_namespace: bool - # If True, then the console will enter in debug mode. - debug: NotRequired[bool] + # If not None, then the console will use an alternative run method. + run_method: NotRequired[str] diff --git a/spyder/plugins/ipythonconsole/plugin.py b/spyder/plugins/ipythonconsole/plugin.py index 895625b50f6..a4dcaf30705 100644 --- a/spyder/plugins/ipythonconsole/plugin.py +++ b/spyder/plugins/ipythonconsole/plugin.py @@ -350,11 +350,6 @@ def on_editor_available(self): editor = self.get_plugin(Plugins.Editor) self.sig_edit_goto_requested.connect(editor.load) self.sig_edit_new.connect(editor.new) - editor.sig_run_file_in_ipyclient.connect( - self.run_script) - editor.sig_run_cell_in_ipyclient.connect( - self.run_cell) - editor.exec_in_extconsole.connect(self.run_selection) editor.add_supported_run_configuration( self.python_editor_run_configuration) @@ -405,11 +400,6 @@ def on_editor_teardown(self): editor = self.get_plugin(Plugins.Editor) self.sig_edit_goto_requested.disconnect(editor.load) self.sig_edit_new.disconnect(editor.new) - editor.sig_run_file_in_ipyclient.disconnect( - self.run_script) - editor.sig_run_cell_in_ipyclient.disconnect( - self.run_cell) - editor.exec_in_extconsole.disconnect(self.run_selection) editor.remove_supported_run_configuration( self.python_editor_run_configuration) @@ -659,7 +649,7 @@ def exec_files( current_client = params['current'] clear_variables = params['clear_namespace'] console_namespace = params['console_namespace'] - debug = params.get('debug', False) + run_method = params.get('run_method', 'runfile') # Escape single and double quotes in fname and dirname. # Fixes spyder-ide/spyder#2158. @@ -678,8 +668,7 @@ def exec_files( current_client, clear_variables, console_namespace, - focus_to_editor=True, - method='debugfile' if debug else 'runfile', + method=run_method, force_wdir=force_wdir ) @@ -705,17 +694,25 @@ def exec_cell( run_input: CellRun = input['run_input'] cell_text = run_input['cell'] + + if run_input['copy']: + self.run_selection(cell_text) + return + cell_name = run_input['cell_name'] filename = run_input['path'] - copy = run_input['copy'] - self.run_cell(cell_text, cell_name, filename, copy, - focus_to_editor=True) + + exec_params = conf['params'] + params: IPythonConsolePyConfiguration = exec_params['executor_params'] + run_method = params.get('run_method', 'runcell') + self.run_cell(cell_text, cell_name, filename, + method=run_method) # ---- For execution and debugging def run_script(self, filename, wdir, args='', post_mortem=False, current_client=True, clear_variables=False, console_namespace=False, - focus_to_editor=True, method=None, + method=None, force_wdir=False): """ Run script in current or dedicated client. @@ -739,8 +736,6 @@ def run_script(self, filename, wdir, args='', False otherwise. console_namespace : bool, optional True if the console namespace should be used, False otherwise. - focus_to_editor: bool, optional - Leave focus in the editor after execution. method : str or None Method to run the file. It must accept the same arguments as `runfile`. @@ -761,34 +756,27 @@ def run_script(self, filename, wdir, args='', current_client, clear_variables, console_namespace, - focus_to_editor, method, force_wdir) - def run_cell(self, code, cell_name, filename, run_cell_copy, - method='runcell', focus_to_editor=False): + def run_cell(self, code, cell_name, filename, + method='runcell'): """ Run cell in current or dedicated client. Parameters ---------- code : str - Piece of code to run that corresponds to a cell in case - `run_cell_copy` is True. + Piece of code to run that corresponds to a cell. cell_name : str or int Cell name or index. filename : str Path of the file where the cell to execute is located. - run_cell_copy : bool - True if the cell should be executed line by line, - False if the provided `function` should be used. method : str, optional - Name handler of the kernel function to be used to execute the cell - in case `run_cell_copy` is False. + Name handler of the kernel function to be used to execute the cell. The default is 'runcell'. - focus_to_editor: bool - Whether to give focus to the editor after running the cell. If - False, focus is given to the console. + set_focus: bool + Whether to give focus to the console after running the cell. Returns ------- @@ -796,8 +784,7 @@ def run_cell(self, code, cell_name, filename, run_cell_copy, """ self.sig_unmaximize_plugin_requested.emit() self.get_widget().run_cell( - code, cell_name, filename, run_cell_copy, method=method, - focus_to_editor=focus_to_editor) + code, cell_name, filename, method=method) def execute_code(self, lines, current_client=True, clear_variables=False): """ @@ -823,10 +810,10 @@ def execute_code(self, lines, current_client=True, clear_variables=False): current_client=current_client, clear_variables=clear_variables) - def run_selection(self, lines, focus_to_editor=True): + def run_selection(self, lines): """Execute selected lines in the current console.""" self.sig_unmaximize_plugin_requested.emit() - self.get_widget().execute_code(lines, set_focus=not focus_to_editor) + self.get_widget().execute_code(lines) # ---- For working directory and path management def set_current_client_working_directory(self, directory): diff --git a/spyder/plugins/ipythonconsole/widgets/debugging.py b/spyder/plugins/ipythonconsole/widgets/debugging.py index 7b9d973d165..d0f9c6e0467 100644 --- a/spyder/plugins/ipythonconsole/widgets/debugging.py +++ b/spyder/plugins/ipythonconsole/widgets/debugging.py @@ -198,8 +198,7 @@ def __init__(self, *args, **kwargs): self._pdb_prompt = (None, None) # prompt, password self._pdb_last_cmd = '' # last command sent to pdb self._pdb_frame_loc = (None, None) # fname, lineno - self._pdb_focus_to_editor = False # Focus to editor after command - # execution + self._pdb_take_focus = True # Focus to shell after command execution # Command queue self._pdb_input_queue = [] # List of (code, hidden, echo_stack_entry) # Temporary flags @@ -270,11 +269,11 @@ def _pdb_cmd_prefix(self): prefix = '!' return prefix - def pdb_execute_command(self, command, focus_to_editor=False): + def pdb_execute_command(self, command): """ Execute a pdb command """ - self._pdb_focus_to_editor = focus_to_editor + self._pdb_take_focus = False self.pdb_execute( self._pdb_cmd_prefix() + command, hidden=False, echo_stack_entry=False, add_history=False) diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index 5e0142fb090..ea7fc403ef0 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -1823,8 +1823,8 @@ def interrupt_kernel(self): client.stop_button_click_handler() # ---- For cells - def run_cell(self, code, cell_name, filename, run_cell_copy, - method='runcell', focus_to_editor=False): + def run_cell(self, code, cell_name, filename, + method='runcell'): """Run cell in current or dedicated client.""" def norm(text): @@ -1840,22 +1840,16 @@ def norm(text): if client is not None: is_spyder_kernel = client.shellwidget.is_spyder_kernel - # Only use copy for runcell - if method == 'runcell' and (run_cell_copy or not is_spyder_kernel): - # Use copy of cell - line = code.strip() - elif is_spyder_kernel: + if is_spyder_kernel: # Use custom function line = (str( "{}({}, '{}')").format( str(method), repr(cell_name), norm(filename).replace("'", r"\'"))) - - if method == "debugcell": - # To keep focus in editor after running debugfile - client.shellwidget._pdb_focus_to_editor = focus_to_editor - # External kernels and run_cell_copy, just execute the code + elif method == 'runcell': + # Use copy of cell + line = code.strip() else: # Can not use custom function on non-spyder kernels client.shellwidget.append_html_message( @@ -1867,16 +1861,10 @@ def norm(text): return try: - self.execute_code(line, set_focus=not focus_to_editor) + self.execute_code(line) except AttributeError: pass - # This is necessary to prevent raising the console if the editor - # and console are tabified next to each other and the 'Maintain - # focus in the editor' option is enabled. - # Fixes spyder-ide/spyder#17028 - if not focus_to_editor: - self.sig_switch_to_plugin_requested.emit() else: # XXX: not sure it can really happen QMessageBox.warning( @@ -1890,7 +1878,7 @@ def norm(text): # ---- For scripts def run_script(self, filename, wdir, args, post_mortem, current_client, - clear_variables, console_namespace, focus_to_editor, + clear_variables, console_namespace, method=None, force_wdir=False): """Run script in current or dedicated client.""" norm = lambda text: remove_backslashes(str(text)) @@ -1933,9 +1921,6 @@ def run_script(self, filename, wdir, args, post_mortem, current_client, line += ", current_namespace=True" line += ")" - if method == "debugfile": - # To keep focus in editor after running debugfile - client.shellwidget._pdb_focus_to_editor = focus_to_editor elif method in ["runfile", "debugfile"]: # External, non spyder-kernels, use %run line = "%run " @@ -1960,8 +1945,7 @@ def run_script(self, filename, wdir, args, post_mortem, current_client, # Fixes spyder-ide/spyder#7293. pass elif current_client: - self.execute_code(line, current_client, clear_variables, - set_focus=not focus_to_editor) + self.execute_code(line, current_client, clear_variables) else: if is_new_client: client.shellwidget.silent_execute('%clear') @@ -1970,16 +1954,12 @@ def run_script(self, filename, wdir, args, post_mortem, current_client, client.shellwidget.sig_prompt_ready.connect( lambda: self.execute_code( line, current_client, clear_variables, - set_focus=not focus_to_editor, shellwidget=client.shellwidget ) ) except AttributeError: pass - # Show plugin after execution if focus is going to be given to it. - if not focus_to_editor: - self.sig_switch_to_plugin_requested.emit() else: # XXX: not sure it can really happen QMessageBox.warning( @@ -2055,7 +2035,7 @@ def update_active_project_path(self, active_project_path): # ---- For execution def execute_code(self, lines, current_client=True, clear_variables=False, - set_focus=True, shellwidget=None): + shellwidget=None): """Execute code instructions.""" if current_client: sw = self.get_current_shellwidget() @@ -2082,19 +2062,6 @@ def execute_code(self, lines, current_client=True, clear_variables=False, except AttributeError: pass - if set_focus: - # The `activateWindow` call below needs to be inside this `if` - # to avoid giving focus to the console when it's undocked, - # users are running code from the editor and the 'Maintain - # focus in the editor' option is enabled. - # Fixes spyder-ide/spyder#3221 - self.activateWindow() - - # Gives focus to the current client - focus_widget = self.get_focus_widget() - if focus_widget: - focus_widget.setFocus() - # ---- For error handling def go_to_error(self, text): """Go to error if relevant""" diff --git a/spyder/plugins/run/api.py b/spyder/plugins/run/api.py index 6813a8e11ea..197c8fbd213 100644 --- a/spyder/plugins/run/api.py +++ b/spyder/plugins/run/api.py @@ -49,6 +49,10 @@ def __getattribute__(self, key: str) -> str: RunContext = RunContextType('context') +RunContext.File = 'file' +RunContext.Cell = 'cell' +RunContext.Selection = 'selection' + RunResultFormat = RunContextType('result display format') RunResultFormat.NoDisplay = 'no_display' diff --git a/spyder/plugins/run/confpage.py b/spyder/plugins/run/confpage.py index 2b7d5af371b..0e19220d0cf 100644 --- a/spyder/plugins/run/confpage.py +++ b/spyder/plugins/run/confpage.py @@ -223,7 +223,22 @@ def setup_page(self): sn_buttons_layout.setColumnStretch(1, 2) sn_buttons_layout.setColumnStretch(2, 1) + # --- Run code tab --- + newcb = self.create_checkbox + saveall_box = newcb(_("Save all files before running script"), + 'save_all_before_run') + run_cell_box = newcb(_("Copy full cell contents to the console when " + "running code cells"), 'run_cell_copy') + + run_layout = QVBoxLayout() + run_layout.addWidget(saveall_box) + run_layout.addWidget(run_cell_box) + + editor_group = QGroupBox(_("Editor interaction")) + editor_group.setLayout(run_layout) + vlayout = QVBoxLayout(self) + vlayout.addWidget(editor_group) vlayout.addWidget(about_label) vlayout.addSpacing(10) vlayout.addWidget(executor_group) diff --git a/spyder/plugins/run/container.py b/spyder/plugins/run/container.py index d55dbcf5f95..acbd3ee1698 100644 --- a/spyder/plugins/run/container.py +++ b/spyder/plugins/run/container.py @@ -130,7 +130,7 @@ def update_actions(self): def gen_anonymous_execution_run( self, context: str, - action_name: Optional[str], + action_name: Optional[str] = None, re_run: bool = False, last_executor_name: Optional[str] = None ) -> Callable: diff --git a/spyder/plugins/run/plugin.py b/spyder/plugins/run/plugin.py index 62c20545615..92166042a26 100644 --- a/spyder/plugins/run/plugin.py +++ b/spyder/plugins/run/plugin.py @@ -761,6 +761,20 @@ def run_configuration( self.get_container().run_configuration( executor_name, config, executor_conf) + def run_current_configuration(self, executor_name: str, context_name: str): + """ + Run executor for context with current configuraation + + Parameters + ---------- + executor_name: str + The name of the run executor to use. + context_name: str + The identifier of the run context. + """ + self.get_container().gen_anonymous_execution_run( + context_name, last_executor_name=executor_name)() + # ------------------------------------------------------------------------- # End of temporary APIs # ------------------------------------------------------------------------- From 7fbf6d895b3840153667463c55f9a8c6d55e096f Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Sun, 19 Feb 2023 09:10:24 +0100 Subject: [PATCH 02/19] move to next line --- spyder/plugins/editor/widgets/editor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spyder/plugins/editor/widgets/editor.py b/spyder/plugins/editor/widgets/editor.py index f6ef870cfd2..54dd9d776cd 100644 --- a/spyder/plugins/editor/widgets/editor.py +++ b/spyder/plugins/editor/widgets/editor.py @@ -2847,6 +2847,8 @@ def run_selection(self): if editor.is_cursor_on_last_line() and text: editor.append(editor.get_line_separator()) + editor.move_cursor_to_next('line', 'down') + return ( text, (line_off_from, line_off_to), (line_col_from, line_col_to), From 87763e786c0d94a56a0c743ffa2f13f836cbe497 Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Sun, 19 Feb 2023 09:53:50 +0100 Subject: [PATCH 03/19] rename run functions --- spyder/plugins/editor/plugin.py | 29 ++++--------------- spyder/plugins/editor/widgets/editor.py | 16 +++++----- .../editor/widgets/tests/test_editor.py | 10 +++---- 3 files changed, 19 insertions(+), 36 deletions(-) diff --git a/spyder/plugins/editor/plugin.py b/spyder/plugins/editor/plugin.py index 95bc9cc3bf7..cf9f6fd24cf 100644 --- a/spyder/plugins/editor/plugin.py +++ b/spyder/plugins/editor/plugin.py @@ -2951,24 +2951,25 @@ def get_run_configuration_per_context( if context == RunContext.Selection: if action_name == 'to line': - ret = editorstack.run_to_line() + ret = editorstack.get_to_current_line() if ret is not None: text, offsets, line_cols, enc = ret else: return elif action_name == 'line': - text, offsets, line_cols, enc = editorstack.run_from_line() + text, offsets, line_cols, enc = ( + editorstack.get_from_current_line()) else: - text, offsets, line_cols, enc = editorstack.run_selection() + text, offsets, line_cols, enc = editorstack.get_selection() context_name = 'Selection' run_input = SelectionRun( path=fname, selection=text, encoding=enc, line_col_bounds=line_cols, character_bounds=offsets) elif context == RunContext.Cell: if re_run: - info = editorstack.re_run_last_cell() + info = editorstack.get_last_cell() else: - info = editorstack.get_cell() + info = editorstack.get_current_cell() text, offsets, line_cols, cell_name, enc = info context_name = 'Cell' copy_cell = self.get_option('run_cell_copy', section='run') @@ -3003,24 +3004,6 @@ def focus_run_configuration(self, uuid: str): if current_fname != fname: editorstack.set_current_filename(fname) - @Slot() - def run_selection(self): - """Run selection or current line in external console""" - editorstack = self.get_current_editorstack() - editorstack.run_selection() - - @Slot() - def run_to_line(self): - """Run all lines from beginning up to current line""" - editorstack = self.get_current_editorstack() - editorstack.run_to_line() - - @Slot() - def run_from_line(self): - """Run all lines from current line to end""" - editorstack = self.get_current_editorstack() - editorstack.run_from_line() - # ------ Code bookmarks @Slot(int) def save_bookmark(self, slot_num): diff --git a/spyder/plugins/editor/widgets/editor.py b/spyder/plugins/editor/widgets/editor.py index 54dd9d776cd..101ef09f560 100644 --- a/spyder/plugins/editor/widgets/editor.py +++ b/spyder/plugins/editor/widgets/editor.py @@ -2785,7 +2785,7 @@ def format_document_or_selection(self, index=None): finfo.editor.format_document_or_range() # ------ Run - def _run_lines_cursor(self, direction): + def _get_lines_cursor(self, direction): """ Select and run all lines from cursor in given direction""" editor = self.get_current_editor() finfo = self.get_current_finfo() @@ -2806,21 +2806,21 @@ def _run_lines_cursor(self, direction): code_text, off_pos, line_col_pos = selection return code_text.rstrip(), off_pos, line_col_pos, enc - def run_to_line(self): + def get_to_current_line(self): """ Run all lines from the beginning up to, but not including, current line. """ - return self._run_lines_cursor(direction='up') + return self._get_lines_cursor(direction='up') - def run_from_line(self): + def get_from_current_line(self): """ Run all lines from and including the current line to the end of the document. """ - return self._run_lines_cursor(direction='down') + return self._get_lines_cursor(direction='down') - def run_selection(self): + def get_selection(self): """ Run selected text or current line in console. @@ -2855,7 +2855,7 @@ def run_selection(self): encoding ) - def get_cell(self): + def get_current_cell(self): """Get current cell attributes.""" text, block, off_pos, line_col_pos = ( self.get_current_editor().get_cell_as_executable_code()) @@ -2875,7 +2875,7 @@ def advance_cell(self, reverse=False): move_func() - def re_run_last_cell(self): + def get_last_cell(self): """Run the previous cell again.""" if self.last_cell_call is None: return diff --git a/spyder/plugins/editor/widgets/tests/test_editor.py b/spyder/plugins/editor/widgets/tests/test_editor.py index 1b8844dc50e..5eb73b40705 100644 --- a/spyder/plugins/editor/widgets/tests/test_editor.py +++ b/spyder/plugins/editor/widgets/tests/test_editor.py @@ -289,7 +289,7 @@ def test_run_top_line(editor_bot, qtbot): editor_stack, editor = editor_bot editor.go_to_line(1) # line number is one based editor.move_cursor(3) - text, _, _, _ = editor_stack.run_selection() + text, _, _, _ = editor_stack.get_selection() assert text == 'a = 1' # check cursor moves to start of next line; note line number is zero based @@ -299,7 +299,7 @@ def test_run_top_line(editor_bot, qtbot): def test_run_last_nonempty_line(editor_bot, qtbot): editor_stack, editor = editor_bot editor.go_to_line(4) - text, _, _, _ = editor_stack.run_selection() + text, _, _, _ = editor_stack.get_selection() assert text == 'x = 2' assert editor.get_cursor_line_column() == (4, 0) # check cursor moves down @@ -307,13 +307,13 @@ def test_run_last_nonempty_line(editor_bot, qtbot): def test_run_empty_line_in_middle(editor_bot, qtbot): editor_stack, editor = editor_bot editor.go_to_line(3) - _, _, _, _ = editor_stack.run_selection() + _, _, _, _ = editor_stack.get_selection() assert editor.get_cursor_line_column() == (3, 0) # check cursor moves down def test_run_last_line_when_empty(editor_bot, qtbot): editor_stack, editor = editor_bot - _, _, _, _ = editor_stack.run_selection() + _, _, _, _ = editor_stack.get_selection() # check cursor doesn't move assert editor.get_cursor_line_column() == (4, 0) @@ -322,7 +322,7 @@ def test_run_last_line_when_nonempty(editor_bot, qtbot): editor_stack, editor = editor_bot editor.stdkey_backspace() # delete empty line at end old_text = editor.toPlainText() - text, _, _, _ = editor_stack.run_selection() + text, _, _, _ = editor_stack.get_selection() assert text == 'x = 2' expected_new_text = old_text + editor.get_line_separator() # check blank line got added From a7107883b40729c45434402262842b2093e2246b Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Sun, 19 Feb 2023 11:19:26 +0100 Subject: [PATCH 04/19] advance line Add context modificator --- spyder/plugins/editor/api/run.py | 9 +++ spyder/plugins/editor/plugin.py | 26 ++++--- spyder/plugins/editor/widgets/editor.py | 25 ++++--- .../editor/widgets/tests/test_editor.py | 5 ++ spyder/plugins/run/api.py | 8 ++- spyder/plugins/run/container.py | 70 +++++++++++-------- spyder/plugins/run/plugin.py | 27 ++++--- spyder/plugins/run/tests/test_run.py | 1 + 8 files changed, 106 insertions(+), 65 deletions(-) diff --git a/spyder/plugins/editor/api/run.py b/spyder/plugins/editor/api/run.py index b9de2ab5ecc..fdcca6a5cef 100644 --- a/spyder/plugins/editor/api/run.py +++ b/spyder/plugins/editor/api/run.py @@ -80,3 +80,12 @@ class CellRun(TypedDict): # True if the text should be copied over. copy: bool + + +class SelectionContextModificator: + ToLine = "up to line" + FromLine = "from line" + + +class ExtraAction: + Advance = "advance" \ No newline at end of file diff --git a/spyder/plugins/editor/plugin.py b/spyder/plugins/editor/plugin.py index cf9f6fd24cf..01e80a05e7e 100644 --- a/spyder/plugins/editor/plugin.py +++ b/spyder/plugins/editor/plugin.py @@ -47,7 +47,8 @@ from spyder.utils.misc import getcwd_or_home from spyder.widgets.findreplace import FindReplace from spyder.plugins.editor.api.run import ( - EditorRunConfiguration, FileRun, SelectionRun, CellRun) + EditorRunConfiguration, FileRun, SelectionRun, CellRun, + SelectionContextModificator, ExtraAction) from spyder.plugins.editor.confpage import EditorConfigPage from spyder.plugins.editor.utils.autosave import AutosaveForPlugin from spyder.plugins.editor.utils.switcher import EditorSwitcherManager @@ -297,7 +298,7 @@ def __init__(self, parent, ignore_last_opened_files=False): register_shortcut=True, add_to_toolbar=True, add_to_menu=True, - extra_action_name='advance' + extra_action_name=ExtraAction.Advance ) run.create_run_button( @@ -318,7 +319,8 @@ def __init__(self, parent, ignore_last_opened_files=False): shortcut_context="_", register_shortcut=True, add_to_toolbar=True, - add_to_menu=True + add_to_menu=True, + extra_action_name=ExtraAction.Advance, ) run.create_run_button( @@ -329,8 +331,7 @@ def __init__(self, parent, ignore_last_opened_files=False): register_shortcut=True, add_to_toolbar=False, add_to_menu=True, - extra_action_name="to line", - conjunction_or_preposition="up" + context_modificator=SelectionContextModificator.ToLine ) run.create_run_button( @@ -341,8 +342,7 @@ def __init__(self, parent, ignore_last_opened_files=False): register_shortcut=True, add_to_toolbar=False, add_to_menu=True, - extra_action_name="line", - conjunction_or_preposition="from" + context_modificator=SelectionContextModificator.FromLine ) layout = QVBoxLayout() @@ -2936,7 +2936,7 @@ def get_run_configuration(self, metadata_id: str) -> RunConfiguration: return run_conf def get_run_configuration_per_context( - self, context, action_name, + self, context, extra_action_name, context_modificator, re_run=False ) -> Optional[RunConfiguration]: editorstack = self.get_current_editorstack() @@ -2950,17 +2950,21 @@ def get_run_configuration_per_context( context_name = None if context == RunContext.Selection: - if action_name == 'to line': + if context_modificator == SelectionContextModificator.ToLine: ret = editorstack.get_to_current_line() if ret is not None: text, offsets, line_cols, enc = ret else: return - elif action_name == 'line': + elif ( + context_modificator == SelectionContextModificator.FromLine + ): text, offsets, line_cols, enc = ( editorstack.get_from_current_line()) else: text, offsets, line_cols, enc = editorstack.get_selection() + if extra_action_name == ExtraAction.Advance: + editorstack.advance_line() context_name = 'Selection' run_input = SelectionRun( path=fname, selection=text, encoding=enc, @@ -2978,7 +2982,7 @@ def get_run_configuration_per_context( line_col_bounds=line_cols, character_bounds=offsets, copy=copy_cell) - if action_name == 'advance': + if extra_action_name == ExtraAction.Advance: editorstack.advance_cell() metadata: RunConfigurationMetadata = { diff --git a/spyder/plugins/editor/widgets/editor.py b/spyder/plugins/editor/widgets/editor.py index 101ef09f560..ddb623f406f 100644 --- a/spyder/plugins/editor/widgets/editor.py +++ b/spyder/plugins/editor/widgets/editor.py @@ -2786,7 +2786,7 @@ def format_document_or_selection(self, index=None): # ------ Run def _get_lines_cursor(self, direction): - """ Select and run all lines from cursor in given direction""" + """ Select and return all lines from cursor in given direction""" editor = self.get_current_editor() finfo = self.get_current_finfo() enc = finfo.encoding @@ -2808,21 +2808,21 @@ def _get_lines_cursor(self, direction): def get_to_current_line(self): """ - Run all lines from the beginning up to, but not including, current + Get all lines from the beginning up to, but not including, current line. """ return self._get_lines_cursor(direction='up') def get_from_current_line(self): """ - Run all lines from and including the current line to the end of + Get all lines from and including the current line to the end of the document. """ return self._get_lines_cursor(direction='down') def get_selection(self): """ - Run selected text or current line in console. + Get selected text or current line in console. If some text is selected, then execute that text in console. @@ -2844,17 +2844,24 @@ def get_selection(self): line = editor.get_current_line() text = line.lstrip() - if editor.is_cursor_on_last_line() and text: - editor.append(editor.get_line_separator()) - - editor.move_cursor_to_next('line', 'down') - return ( text, (line_off_from, line_off_to), (line_col_from, line_col_to), encoding ) + def advance_line(self): + """Advance to the next line.""" + editor = self.get_current_editor() + if ( + editor.is_cursor_on_last_line() + and editor.get_current_line().strip() + ): + editor.append(editor.get_line_separator()) + + editor.move_cursor_to_next('line', 'down') + + def get_current_cell(self): """Get current cell attributes.""" text, block, off_pos, line_col_pos = ( diff --git a/spyder/plugins/editor/widgets/tests/test_editor.py b/spyder/plugins/editor/widgets/tests/test_editor.py index 5eb73b40705..5276582a15d 100644 --- a/spyder/plugins/editor/widgets/tests/test_editor.py +++ b/spyder/plugins/editor/widgets/tests/test_editor.py @@ -290,6 +290,7 @@ def test_run_top_line(editor_bot, qtbot): editor.go_to_line(1) # line number is one based editor.move_cursor(3) text, _, _, _ = editor_stack.get_selection() + editor_stack.advance_line() assert text == 'a = 1' # check cursor moves to start of next line; note line number is zero based @@ -300,6 +301,7 @@ def test_run_last_nonempty_line(editor_bot, qtbot): editor_stack, editor = editor_bot editor.go_to_line(4) text, _, _, _ = editor_stack.get_selection() + editor_stack.advance_line() assert text == 'x = 2' assert editor.get_cursor_line_column() == (4, 0) # check cursor moves down @@ -308,12 +310,14 @@ def test_run_empty_line_in_middle(editor_bot, qtbot): editor_stack, editor = editor_bot editor.go_to_line(3) _, _, _, _ = editor_stack.get_selection() + editor_stack.advance_line() assert editor.get_cursor_line_column() == (3, 0) # check cursor moves down def test_run_last_line_when_empty(editor_bot, qtbot): editor_stack, editor = editor_bot _, _, _, _ = editor_stack.get_selection() + editor_stack.advance_line() # check cursor doesn't move assert editor.get_cursor_line_column() == (4, 0) @@ -323,6 +327,7 @@ def test_run_last_line_when_nonempty(editor_bot, qtbot): editor.stdkey_backspace() # delete empty line at end old_text = editor.toPlainText() text, _, _, _ = editor_stack.get_selection() + editor_stack.advance_line() assert text == 'x = 2' expected_new_text = old_text + editor.get_line_separator() # check blank line got added diff --git a/spyder/plugins/run/api.py b/spyder/plugins/run/api.py index 197c8fbd213..f467313e9ec 100644 --- a/spyder/plugins/run/api.py +++ b/spyder/plugins/run/api.py @@ -309,7 +309,8 @@ def get_run_configuration(self, uuid: str) -> RunConfiguration: def get_run_configuration_per_context( self, context: str, - action_name: Optional[str] = None, + extra_action_name: Optional[str] = None, + context_modificator: Optional[str] = None, re_run: bool = False ) -> Optional[RunConfiguration]: """ @@ -323,10 +324,13 @@ def get_run_configuration_per_context( context: str The context identifier for which the run configuration is requested. - action_name: Optional[str] + extra_action_name: Optional[str] If not None, the name of the action that the provider should take after gathering the run configuration input. Else, no action needs to take place. + context_modificator: Optional[str] + str describing how to alter the context. + e.g. run selection re_run: bool If True, then the requested configuration should correspond to the last executed configuration for the given context. diff --git a/spyder/plugins/run/container.py b/spyder/plugins/run/container.py index acbd3ee1698..e64c7ef7e39 100644 --- a/spyder/plugins/run/container.py +++ b/spyder/plugins/run/container.py @@ -110,9 +110,9 @@ def setup(self): self.current_input_extension: Optional[str] = None self.context_actions: Dict[ - Tuple[str, str], Tuple[QAction, Callable]] = {} + Tuple[str, str, str], Tuple[QAction, Callable]] = {} self.re_run_actions: Dict[ - Tuple[str, str], Tuple[QAction, Callable]] = {} + Tuple[str, str, str], Tuple[QAction, Callable]] = {} self.run_executor_actions: Dict[ Tuple[str, str], Tuple[QAction, Callable]] = {} @@ -130,7 +130,8 @@ def update_actions(self): def gen_anonymous_execution_run( self, context: str, - action_name: Optional[str] = None, + extra_action_name: Optional[str] = None, + context_modificator: Optional[str] = None, re_run: bool = False, last_executor_name: Optional[str] = None ) -> Callable: @@ -144,7 +145,8 @@ def anonymous_execution_run(): self.currently_selected_configuration) else: run_conf = input_provider.get_run_configuration_per_context( - context, action_name, re_run=re_run) + context, extra_action_name, context_modificator, + re_run=re_run) if run_conf is None: return @@ -190,8 +192,12 @@ def anonymous_execution_run(): self.last_executed_per_context |= {(uuid, context)} - if (context, action_name) in self.re_run_actions: - act, _ = self.re_run_actions[(context, action_name)] + if ( + (context, extra_action_name, context_modificator) + in self.re_run_actions + ): + act, _ = self.re_run_actions[ + (context, extra_action_name, context_modificator)] act.setEnabled(True) return anonymous_execution_run @@ -303,12 +309,12 @@ def switch_focused_run_configuration(self, uuid: Optional[str]): elif uuid is None: self.run_action.setEnabled(False) - for context, act in self.context_actions: - action, __ = self.context_actions[(context, act)] + for context, act, mod in self.context_actions: + action, __ = self.context_actions[(context, act, mod)] action.setEnabled(False) - for context, act in self.re_run_actions: - action, __ = self.re_run_actions[(context, act)] + for context, act, mod in self.re_run_actions: + action, __ = self.re_run_actions[(context, act, mod)] action.setEnabled(False) for context_name, executor_name in self.run_executor_actions: @@ -326,14 +332,14 @@ def set_actions_status(self): input_provider_ext_ctxs = self.supported_extension_contexts[ self.current_input_provider] - for context, act in self.context_actions: + for context, act, mod in self.context_actions: key = (self.current_input_extension, context) status = key in self.executor_model status = status and key in input_provider_ext_ctxs - action, __ = self.context_actions[(context, act)] + action, __ = self.context_actions[(context, act, mod)] action.setEnabled(status) - for context, act in self.re_run_actions: + for context, act, mod in self.re_run_actions: key = (self.current_input_extension, context) status = key in self.executor_model status = status and key in input_provider_ext_ctxs @@ -342,7 +348,7 @@ def set_actions_status(self): (self.currently_selected_configuration, context) in self.last_executed_per_context) - action, __ = self.re_run_actions[(context, act)] + action, __ = self.re_run_actions[(context, act, mod)] action.setEnabled(status and last_run_exists) for context_name, executor_name in self.run_executor_actions: @@ -368,7 +374,7 @@ def create_run_button( shortcut_context: Optional[str] = None, register_shortcut: bool = False, extra_action_name: Optional[str] = None, - conjunction_or_preposition: str = "and", + context_modificator: Optional[str] = None, re_run: bool = False ) -> QAction: """ @@ -393,10 +399,9 @@ def create_run_button( extra_action_name: Optional[str] The name of the action to execute on the run input provider after requesting the run input. - conjunction_or_preposition: str - The conjunction or preposition used to describe the action that - should take place after the context, e.g. run advance, - run selection the current line, etc. Default: and + context_modificator: Optional[str] + The name of the modification to apply to the action. + e.g. run selection re_run: bool If True, then the button will act as a re-run button instead of a run one. @@ -414,7 +419,8 @@ def create_run_button( Cell can be used if and only if the file was registered. 2. The button will be registered as `run ` or - `run and ` on the action registry. + `run and ` + on the action registry. 3. The created button will operate over the last focused run input provider. @@ -426,20 +432,24 @@ def create_run_button( shortcuts. """ dict_actions = self.re_run_actions if re_run else self.context_actions - if (context_name, extra_action_name) in dict_actions: + if ( + (context_name, extra_action_name, context_modificator) + in dict_actions + ): action, __ = self.context_actions[ - (context_name, extra_action_name)] + (context_name, extra_action_name, context_modificator)] return action prefix = 're-' if re_run else '' action_name = f'{prefix}run {context_name}' + if context_modificator is not None: + action_name = f'{action_name} {context_modificator}' if extra_action_name is not None: - action_name = (f'{action_name} {conjunction_or_preposition} ' - f'{extra_action_name}') + action_name = f'{action_name} and {extra_action_name}' func = self.gen_anonymous_execution_run( - context_name, extra_action_name, re_run=re_run, - last_executor_name=None) + context_name, extra_action_name, context_modificator, + re_run=re_run, last_executor_name=None) action = self.create_action( action_name, @@ -453,11 +463,13 @@ def create_run_button( ) if re_run: - self.re_run_actions[(context_name, extra_action_name)] = ( + self.re_run_actions[ + (context_name, extra_action_name, context_modificator)] = ( action, func) action.setEnabled(False) else: - self.context_actions[(context_name, extra_action_name)] = ( + self.context_actions[ + (context_name, extra_action_name, context_modificator)] = ( action, func) self.sig_run_action_created.emit(action_name, register_shortcut, @@ -531,7 +543,7 @@ def create_run_in_executor_button( selected_executor=executor_name) else: func = self.gen_anonymous_execution_run( - context_name, None, re_run=False, + context_name, re_run=False, last_executor_name=executor_name) action = self.create_action( diff --git a/spyder/plugins/run/plugin.py b/spyder/plugins/run/plugin.py index 92166042a26..a8f59eebeca 100644 --- a/spyder/plugins/run/plugin.py +++ b/spyder/plugins/run/plugin.py @@ -395,7 +395,7 @@ def create_run_button( shortcut_context: Optional[str] = None, register_shortcut: bool = False, extra_action_name: Optional[str] = None, - conjunction_or_preposition: str = "and", + context_modificator: Optional[str] = None, add_to_toolbar: bool = False, add_to_menu: bool = False, re_run: bool = False @@ -422,10 +422,9 @@ def create_run_button( extra_action_name: Optional[str] The name of the action to execute on the run input provider after requesting the run input. - conjunction_or_preposition: str - The conjunction or preposition used to describe the action that - should take place after the context, e.g. run advance, - run selection the current line, etc. Default: "and". + context_modificator: Optional[str] + The name of the modification to apply to the action. + e.g. run selection add_to_toolbar: bool If True, then the action will be added to the Run section of the main toolbar. @@ -448,7 +447,8 @@ def create_run_button( Cell can be used if and only if the file was registered. 2. The button will be registered as `run ` or - `run and ` on the action registry. + `run and ` + on the action registry. 3. The created button will operate over the last focused run input provider. @@ -459,7 +459,7 @@ def create_run_button( selection), the editor will register their corresponding icons and shortcuts. """ - key = (context_name, extra_action_name, conjunction_or_preposition, + key = (context_name, extra_action_name, context_modificator, re_run) action = self.get_container().create_run_button( @@ -470,7 +470,7 @@ def create_run_button( shortcut_context=shortcut_context, register_shortcut=register_shortcut, extra_action_name=extra_action_name, - conjunction_or_preposition=conjunction_or_preposition, + context_modificator=context_modificator, re_run=re_run ) @@ -510,7 +510,7 @@ def destroy_run_button( self, context_name: str, extra_action_name: Optional[str] = None, - conjunction_or_preposition: str = "and", + context_modificator: Optional[str] = None, re_run: bool = False ): """ @@ -524,10 +524,9 @@ def destroy_run_button( extra_action_name: Optional[str] The name of the action to execute on the run input provider after requesting the run input. - conjunction_or_preposition: str - The conjunction or preposition used to describe the action that - should take place after the context, i.e. run advance, - run selection the current line, etc. Default: "and". + context_modificator: Optional[str] + The name of the modification to apply to the action. + e.g. run selection re_run: bool If True, then the button was registered as a re-run button instead of a run one. @@ -542,7 +541,7 @@ def destroy_run_button( toolbar = self.get_plugin(Plugins.Toolbar) shortcuts = self.get_plugin(Plugins.Shortcuts) - key = (context_name, extra_action_name, conjunction_or_preposition, + key = (context_name, extra_action_name, context_modificator, re_run) with self.action_lock: diff --git a/spyder/plugins/run/tests/test_run.py b/spyder/plugins/run/tests/test_run.py index deeb7e4e49f..53b25b760d7 100644 --- a/spyder/plugins/run/tests/test_run.py +++ b/spyder/plugins/run/tests/test_run.py @@ -133,6 +133,7 @@ def get_run_configuration_per_context( self, context: str, action_name: Optional[str] = None, + context_modificator: Optional[str] = None, re_run: bool = False ) -> Optional[RunConfiguration]: From 50a634901ad053aaf6d355ee9e621e261080209b Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Sun, 19 Feb 2023 12:49:32 +0100 Subject: [PATCH 05/19] fix test --- spyder/app/tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spyder/app/tests/conftest.py b/spyder/app/tests/conftest.py index 85c3aa7fbd9..557ac96ada3 100755 --- a/spyder/app/tests/conftest.py +++ b/spyder/app/tests/conftest.py @@ -415,7 +415,7 @@ def main_window(request, tmpdir, qtbot): run_cell_and_advance_action) window.run_cell_and_advance_button = run_cell_and_advance_button - run_selection_action = window.run.get_action('run selection') + run_selection_action = window.run.get_action('run selection and advance') run_selection_button = run_toolbar.widgetForAction(run_selection_action) window.run_selection_button = run_selection_button From 066c5cee407e36a4166612e0874b301deef85155 Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Sun, 19 Feb 2023 18:33:03 +0100 Subject: [PATCH 06/19] use create_run_in_executor_button --- spyder/api/widgets/mixins.py | 5 +- spyder/config/main.py | 6 +- spyder/plugins/debugger/api.py | 2 +- spyder/plugins/debugger/plugin.py | 151 ++++++++---------- .../plugins/debugger/widgets/main_widget.py | 45 +----- spyder/plugins/run/plugin.py | 97 ++++++----- 6 files changed, 137 insertions(+), 169 deletions(-) diff --git a/spyder/api/widgets/mixins.py b/spyder/api/widgets/mixins.py index 0aba120d725..e53f683f83c 100644 --- a/spyder/api/widgets/mixins.py +++ b/spyder/api/widgets/mixins.py @@ -137,13 +137,14 @@ class SpyderToolbarMixin: """ def add_item_to_toolbar(self, action_or_widget, toolbar, section=None, - before=None): + before=None, before_section=None): """ If you provide a `before` action, the action will be placed before this one, so the section option will be ignored, since the action will now be placed in the same section as the `before` action. """ - toolbar.add_item(action_or_widget, section=section, before=before) + toolbar.add_item(action_or_widget, section=section, before=before, + before_section=before_section) def create_stretcher(self, id_=None): """ diff --git a/spyder/config/main.py b/spyder/config/main.py index a92ccfd058a..168cd65abe8 100644 --- a/spyder/config/main.py +++ b/spyder/config/main.py @@ -526,9 +526,9 @@ # ---- In widgets/debugger/framesbrowser.py ---- 'debugger/refresh': 'Ctrl+R', 'debugger/search': 'Ctrl+F', - 'debugger/debug file': "Ctrl+F5", - 'debugger/debug cell': 'Alt+Shift+Return', - 'debugger/debug selection': '', + 'debugger/run file in debugger': "Ctrl+F5", + 'debugger/run cell in debugger': 'Alt+Shift+Return', + 'debugger/run selection in debugger': '', 'debugger/next': "Ctrl+F10", 'debugger/continue': "Ctrl+F12", 'debugger/step': "Ctrl+F11", diff --git a/spyder/plugins/debugger/api.py b/spyder/plugins/debugger/api.py index bed90d9927a..40f13860ab6 100644 --- a/spyder/plugins/debugger/api.py +++ b/spyder/plugins/debugger/api.py @@ -9,7 +9,7 @@ """ from spyder.plugins.debugger.widgets.main_widget import ( - DebuggerWidgetActions, DebuggerToolbarActions, DebuggerBreakpointActions, + DebuggerWidgetActions, DebuggerBreakpointActions, DebuggerWidgetOptionsMenuSections, DebuggerWidgetMainToolBarSections, DebuggerWidgetMenus, DebuggerContextMenuSections, DebuggerContextMenuActions) diff --git a/spyder/plugins/debugger/plugin.py b/spyder/plugins/debugger/plugin.py index ace00ecdf94..6da92ac2b98 100644 --- a/spyder/plugins/debugger/plugin.py +++ b/spyder/plugins/debugger/plugin.py @@ -24,16 +24,15 @@ from spyder.plugins.debugger.utils.breakpointsmanager import ( BreakpointsManager, clear_all_breakpoints, clear_breakpoint) from spyder.plugins.debugger.widgets.main_widget import ( - DebuggerBreakpointActions, DebuggerToolbarActions, DebuggerWidget, - DebuggerWidgetActions) + DebuggerBreakpointActions, DebuggerWidget, + DebuggerWidgetActions, DebuggerWidgetMainToolBarSections) from spyder.plugins.editor.utils.editor import get_file_language from spyder.plugins.editor.utils.languages import ALL_LANGUAGES from spyder.plugins.ipythonconsole.api import IPythonConsolePyConfiguration from spyder.plugins.mainmenu.api import ApplicationMenus, DebugMenuSections from spyder.plugins.run.api import ( - WorkingDirOpts, WorkingDirSource, RunExecutionParameters, RunConfiguration, - ExtendedRunExecutionParameters, RunExecutor, run_execute, RunContext, - RunResult) + RunConfiguration, ExtendedRunExecutionParameters, RunExecutor, run_execute, + RunContext, RunResult) from spyder.plugins.toolbar.api import ApplicationToolbars from spyder.plugins.ipythonconsole.widgets.config import IPythonConfigOptions from spyder.plugins.editor.api.run import CellRun, SelectionRun @@ -48,8 +47,7 @@ class Debugger(SpyderDockablePlugin, ShellConnectMixin, RunExecutor): NAME = 'debugger' REQUIRES = [Plugins.IPythonConsole, Plugins.Preferences, Plugins.Run] - OPTIONAL = [Plugins.Editor, Plugins.MainMenu, Plugins.Toolbar, - Plugins.VariableExplorer] + OPTIONAL = [Plugins.Editor, Plugins.MainMenu, Plugins.VariableExplorer] TABIFY = [Plugins.VariableExplorer, Plugins.Help] WIDGET_CLASS = DebuggerWidget CONF_SECTION = NAME @@ -73,9 +71,6 @@ def on_initialize(self): widget = self.get_widget() widget.sig_pdb_state_changed.connect( self._update_current_codeeditor_pdb_state) - widget.sig_debug_file.connect(self.debug_file) - widget.sig_debug_cell.connect(self.debug_cell) - widget.sig_debug_selection.connect(self.debug_selection) widget.sig_toggle_breakpoints.connect(self._set_or_clear_breakpoint) widget.sig_toggle_conditional_breakpoints.connect( self._set_or_edit_conditional_breakpoint) @@ -138,6 +133,69 @@ def on_run_available(self): run = self.get_plugin(Plugins.Run) run.register_executor_configuration(self, self.executor_configuration) + debug_file_action = run.create_run_in_executor_button( + RunContext.File, + self.NAME, + text=_("&Debug file"), + tip=_("Debug file"), + icon=self.create_icon('debug'), + shortcut_context=self.NAME, + register_shortcut=True, + add_to_menu=( + ApplicationMenus.Debug, + DebugMenuSections.StartDebug, + DebugMenuSections.ControlDebug + ), + add_to_toolbar=ApplicationToolbars.Debug + ) + + debug_cell_action = run.create_run_in_executor_button( + RunContext.Cell, + self.NAME, + text=_("Debug cell"), + tip=_("Debug cell"), + icon=self.create_icon('debug_cell'), + shortcut_context=self.NAME, + register_shortcut=True, + add_to_menu=( + ApplicationMenus.Debug, + DebugMenuSections.StartDebug, + DebugMenuSections.ControlDebug + ), + add_to_toolbar=ApplicationToolbars.Debug + ) + + debug_selection_action = run.create_run_in_executor_button( + RunContext.Selection, + self.NAME, + text=_("Debug selection or current line"), + tip=_("Debug selection or current line"), + icon=self.create_icon('debug_selection'), + shortcut_context=self.NAME, + register_shortcut=True, + add_to_menu=( + ApplicationMenus.Debug, + DebugMenuSections.StartDebug, + DebugMenuSections.ControlDebug + ), + add_to_toolbar=ApplicationToolbars.Debug + ) + + widget = self.get_widget() + main_toolbar = widget.get_main_toolbar() + + for item in [ + debug_file_action, + debug_cell_action, + debug_selection_action, + ]: + widget.add_item_to_toolbar( + item, + toolbar=main_toolbar, + section=DebuggerWidgetMainToolBarSections.Run, + before_section=DebuggerWidgetMainToolBarSections.Main, + ) + @on_plugin_teardown(plugin=Plugins.Run) def on_run_teardown(self): run = self.get_plugin(Plugins.Run) @@ -170,9 +228,6 @@ def on_editor_available(self): # Apply shortcuts to editor and add actions to pythonfile list editor_shortcuts = [ - DebuggerToolbarActions.DebugCurrentFile, - DebuggerToolbarActions.DebugCurrentCell, - DebuggerToolbarActions.DebugCurrentSelection, DebuggerBreakpointActions.ToggleBreakpoint, DebuggerBreakpointActions.ToggleConditionalBreakpoint, ] @@ -202,9 +257,6 @@ def on_editor_teardown(self): # Remove editor actions editor_shortcuts = [ - DebuggerToolbarActions.DebugCurrentFile, - DebuggerToolbarActions.DebugCurrentCell, - DebuggerToolbarActions.DebugCurrentSelection, DebuggerBreakpointActions.ToggleBreakpoint, DebuggerBreakpointActions.ToggleConditionalBreakpoint, ] @@ -226,16 +278,6 @@ def on_variable_explorer_teardown(self): def on_main_menu_available(self): mainmenu = self.get_plugin(Plugins.MainMenu) - # StartDebug section - for action in [DebuggerToolbarActions.DebugCurrentFile, - DebuggerToolbarActions.DebugCurrentCell, - DebuggerToolbarActions.DebugCurrentSelection]: - mainmenu.add_item_to_application_menu( - self.get_action(action), - menu_id=ApplicationMenus.Debug, - section=DebugMenuSections.StartDebug, - before_section=DebugMenuSections.ControlDebug) - # ControlDebug section for action in [DebuggerWidgetActions.Next, DebuggerWidgetActions.Step, @@ -263,9 +305,6 @@ def on_main_menu_teardown(self): mainmenu = self.get_plugin(Plugins.MainMenu) names = [ - DebuggerToolbarActions.DebugCurrentFile, - DebuggerToolbarActions.DebugCurrentCell, - DebuggerToolbarActions.DebugCurrentSelection, DebuggerWidgetActions.Next, DebuggerWidgetActions.Step, DebuggerWidgetActions.Return, @@ -281,30 +320,6 @@ def on_main_menu_teardown(self): menu_id=ApplicationMenus.Debug ) - @on_plugin_available(plugin=Plugins.Toolbar) - def on_toolbar_available(self): - toolbar = self.get_plugin(Plugins.Toolbar) - - for action in [DebuggerToolbarActions.DebugCurrentFile, - DebuggerToolbarActions.DebugCurrentCell, - DebuggerToolbarActions.DebugCurrentSelection]: - toolbar.add_item_to_application_toolbar( - self.get_action(action), - toolbar_id=ApplicationToolbars.Debug - ) - - @on_plugin_teardown(plugin=Plugins.Toolbar) - def on_toolbar_teardown(self): - toolbar = self.get_plugin(Plugins.Toolbar) - - for action in [DebuggerToolbarActions.DebugCurrentFile, - DebuggerToolbarActions.DebugCurrentCell, - DebuggerToolbarActions.DebugCurrentSelection]: - toolbar.remove_item_from_application_toolbar( - action, - toolbar_id=ApplicationToolbars.Debug - ) - # ---- Private API # ------------------------------------------------------------------------ def _load_pdb_file_in_editor(self, fname, lineno): @@ -572,33 +587,3 @@ def exec_selection( console.exec_selection(input, conf) self.get_widget().set_pdb_take_focus(False) - - @Slot() - def debug_file(self): - """ - Debug current file. - """ - run = self.get_plugin(Plugins.Run) - if run is None: - return - run.run_current_configuration(self.NAME, RunContext.File) - - @Slot() - def debug_cell(self): - """ - Debug current cell. - """ - run = self.get_plugin(Plugins.Run) - if run is None: - return - run.run_current_configuration(self.NAME, RunContext.Cell) - - @Slot() - def debug_selection(self): - """ - Debug current selection or line. - """ - run = self.get_plugin(Plugins.Run) - if run is None: - return - run.run_current_configuration(self.NAME, RunContext.Selection) diff --git a/spyder/plugins/debugger/widgets/main_widget.py b/spyder/plugins/debugger/widgets/main_widget.py index 75c180b5f78..ccfb744e3c3 100644 --- a/spyder/plugins/debugger/widgets/main_widget.py +++ b/spyder/plugins/debugger/widgets/main_widget.py @@ -44,12 +44,6 @@ class DebuggerWidgetActions: ToggleLocalsOnClick = 'toggle_show_locals_on_click_action' -class DebuggerToolbarActions: - DebugCurrentFile = 'debug file' - DebugCurrentCell = 'debug cell' - DebugCurrentSelection = 'debug selection' - - class DebuggerBreakpointActions: ToggleBreakpoint = 'toggle breakpoint' ToggleConditionalBreakpoint = 'toggle conditional breakpoint' @@ -62,6 +56,7 @@ class DebuggerWidgetOptionsMenuSections: class DebuggerWidgetMainToolBarSections: + Run = "run" Main = 'main_section' @@ -114,14 +109,6 @@ class DebuggerWidget(ShellConnectMainWidget): The shellwidget the request originated from """ - sig_debug_file = Signal() - """This signal is emitted to request the current file to be debugged.""" - - sig_debug_cell = Signal() - """This signal is emitted to request the current cell to be debugged.""" - sig_debug_selection = Signal() - """This signal is emitted to request the current line to be debugged.""" - sig_breakpoints_saved = Signal() """Breakpoints have been saved""" @@ -273,33 +260,6 @@ def setup(self): register_shortcut=True ) - debug_file_action = self.create_action( - DebuggerToolbarActions.DebugCurrentFile, - text=_("&Debug file"), - tip=_("Debug file"), - icon=self.create_icon('debug'), - triggered=self.sig_debug_file, - register_shortcut=True, - ) - - debug_cell_action = self.create_action( - DebuggerToolbarActions.DebugCurrentCell, - text=_("Debug cell"), - tip=_("Debug cell"), - icon=self.create_icon('debug_cell'), - triggered=self.sig_debug_cell, - register_shortcut=True, - ) - - debug_selection_action = self.create_action( - DebuggerToolbarActions.DebugCurrentSelection, - text=_("Debug selection or current line"), - tip=_("Debug selection or current line"), - icon=self.create_icon('debug_selection'), - triggered=self.sig_debug_selection, - register_shortcut=True, - ) - self.create_action( DebuggerBreakpointActions.ToggleBreakpoint, text=_("Set/Clear breakpoint"), @@ -350,9 +310,6 @@ def setup(self): secondary_toolbar = self.create_toolbar("widget_control") for item in [ - debug_file_action, - debug_cell_action, - debug_selection_action, enter_debug_action, inspect_action, ]: diff --git a/spyder/plugins/run/plugin.py b/spyder/plugins/run/plugin.py index a8f59eebeca..1e9d00d79dd 100644 --- a/spyder/plugins/run/plugin.py +++ b/spyder/plugins/run/plugin.py @@ -125,11 +125,13 @@ def on_main_menu_available(self): ) while self.pending_menu_actions != []: - action = self.pending_menu_actions.pop(0) + action, menu_id, menu_section, before_section = ( + self.pending_menu_actions.pop(0)) main_menu.add_item_to_application_menu( action, - ApplicationMenus.Run, - RunMenuSections.RunExtras + menu_id, + menu_section, + before_section=before_section ) @on_plugin_available(plugin=Plugins.Preferences) @@ -144,9 +146,9 @@ def on_toolbar_available(self): self.get_action(RunActions.Run), ApplicationToolbars.Run) while self.pending_toolbar_actions != []: - action = self.pending_toolbar_actions.pop(0) + action, toolbar_id = self.pending_toolbar_actions.pop(0) toolbar.add_item_to_application_toolbar( - action, ApplicationToolbars.Run) + action, toolbar_id) @on_plugin_available(plugin=Plugins.Shortcuts) def on_shortcuts_available(self): @@ -396,8 +398,8 @@ def create_run_button( register_shortcut: bool = False, extra_action_name: Optional[str] = None, context_modificator: Optional[str] = None, - add_to_toolbar: bool = False, - add_to_menu: bool = False, + add_to_toolbar: object = False, + add_to_menu: object = False, re_run: bool = False ) -> QAction: """ @@ -425,11 +427,13 @@ def create_run_button( context_modificator: Optional[str] The name of the modification to apply to the action. e.g. run selection - add_to_toolbar: bool + add_to_toolbar: object If True, then the action will be added to the Run section of the - main toolbar. - add_to_menu: bool + main toolbar. If a string, it will be a toolbat id + add_to_menu: object If True, then the action will be added to the Run menu. + If a tuple of 3 strings, it corresponds to + (menu_id, menu_section, before_section) re_run: bool If True, then the button will act as a re-run button instead of a run one. @@ -475,24 +479,38 @@ def create_run_button( ) if add_to_toolbar: + toolbar_id = ApplicationToolbars.Run + if isinstance(add_to_toolbar, str): + toolbar_id = add_to_toolbar toolbar = self.get_plugin(Plugins.Toolbar) if toolbar: toolbar.add_item_to_application_toolbar( - action, ApplicationToolbars.Run) + action, toolbar_id) else: - self.pending_toolbar_actions.append(action) + self.pending_toolbar_actions.append((action, toolbar_id)) self.toolbar_actions |= {key} if add_to_menu: + menu_id, menu_section, before_section = ( + ApplicationMenus.Run, RunMenuSections.RunExtras, + RunMenuSections.RunInExecutors + ) + if isinstance(add_to_menu, tuple): + menu_id, menu_section, before_section = add_to_menu main_menu = self.get_plugin(Plugins.MainMenu) if main_menu: main_menu.add_item_to_application_menu( - action, ApplicationMenus.Run, RunMenuSections.RunExtras, - before_section=RunMenuSections.RunInExecutors + action, menu_id, menu_section, + before_section=before_section ) else: - self.pending_menu_actions.append(action) + self.pending_menu_actions.append(( + action, + menu_id, + menu_section, + before_section + )) self.menu_actions |= {key} @@ -575,8 +593,8 @@ def create_run_in_executor_button( tip: Optional[str] = None, shortcut_context: Optional[str] = None, register_shortcut: bool = False, - add_to_toolbar: bool = False, - add_to_menu: bool = False + add_to_toolbar: object = False, + add_to_menu: object = False ) -> QAction: """ Create a "run in " button for a given run context @@ -599,6 +617,13 @@ def create_run_in_executor_button( register_shortcut: bool If True, main window will expose the shortcut in Preferences. The default value is `False`. + add_to_toolbar: object + If True, then the action will be added to the Run section of the + main toolbar. If a string, it will be a toolbat id + add_to_menu: object + If True, then the action will be added to the Run menu. + If a tuple of 3 strings, it corresponds to + (menu_id, menu_section, before_section) Returns ------- @@ -635,24 +660,38 @@ def create_run_in_executor_button( ) if add_to_toolbar: + toolbar_id = ApplicationToolbars.Run + if isinstance(add_to_toolbar, str): + toolbar_id = add_to_toolbar toolbar = self.get_plugin(Plugins.Toolbar) if toolbar: toolbar.add_item_to_application_toolbar( - action, ApplicationToolbars.Run) + action, toolbar_id) else: - self.pending_toolbar_actions.append(action) + self.pending_toolbar_actions.append((action, toolbar_id)) self.toolbar_actions |= {key} if add_to_menu: + menu_id, menu_section, before_section = ( + ApplicationMenus.Run, RunMenuSections.RunExtras, + RunMenuSections.RunInExecutors + ) + if isinstance(add_to_menu, tuple): + menu_id, menu_section, before_section = add_to_menu main_menu = self.get_plugin(Plugins.MainMenu) if main_menu: main_menu.add_item_to_application_menu( - action, ApplicationMenus.Run, - RunMenuSections.RunInExecutors + action, menu_id, menu_section, + before_section=before_section ) else: - self.pending_menu_actions.append(action) + self.pending_menu_actions.append(( + action, + menu_id, + menu_section, + before_section + )) self.menu_actions |= {key} @@ -760,20 +799,6 @@ def run_configuration( self.get_container().run_configuration( executor_name, config, executor_conf) - def run_current_configuration(self, executor_name: str, context_name: str): - """ - Run executor for context with current configuraation - - Parameters - ---------- - executor_name: str - The name of the run executor to use. - context_name: str - The identifier of the run context. - """ - self.get_container().gen_anonymous_execution_run( - context_name, last_executor_name=executor_name)() - # ------------------------------------------------------------------------- # End of temporary APIs # ------------------------------------------------------------------------- From 63d6af515f863a39b2e9eefca22ed3ef2ec34a18 Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Sun, 19 Feb 2023 18:39:00 +0100 Subject: [PATCH 07/19] fix focus --- spyder/plugins/debugger/widgets/framesbrowser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spyder/plugins/debugger/widgets/framesbrowser.py b/spyder/plugins/debugger/widgets/framesbrowser.py index 0350ed21ef4..1bf8e2c46db 100644 --- a/spyder/plugins/debugger/widgets/framesbrowser.py +++ b/spyder/plugins/debugger/widgets/framesbrowser.py @@ -106,7 +106,7 @@ def pdb_has_stopped(self, fname, lineno): """Handle pdb has stopped""" # this will set the focus to the editor self.sig_load_pdb_file.emit(fname, lineno) - if self.shellwidget._pdb_take_focus: + if not self.shellwidget._pdb_take_focus: # Not taking focus will be required on each call to the debugger self.shellwidget._pdb_take_focus = True else: From fff8f4a56e5c1f0979f3e44a4748a1dfddf50dea Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Sun, 19 Feb 2023 18:43:46 +0100 Subject: [PATCH 08/19] render section --- spyder/plugins/debugger/plugin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/spyder/plugins/debugger/plugin.py b/spyder/plugins/debugger/plugin.py index 6da92ac2b98..30ce6dec771 100644 --- a/spyder/plugins/debugger/plugin.py +++ b/spyder/plugins/debugger/plugin.py @@ -195,6 +195,7 @@ def on_run_available(self): section=DebuggerWidgetMainToolBarSections.Run, before_section=DebuggerWidgetMainToolBarSections.Main, ) + main_toolbar._render() @on_plugin_teardown(plugin=Plugins.Run) def on_run_teardown(self): From e13e75d26933435aeff6c1a3274cefe748da4254 Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Sun, 19 Feb 2023 19:07:20 +0100 Subject: [PATCH 09/19] fix tests --- spyder/app/tests/conftest.py | 3 +-- spyder/app/tests/test_mainwindow.py | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/spyder/app/tests/conftest.py b/spyder/app/tests/conftest.py index 557ac96ada3..44fc12a30fb 100755 --- a/spyder/app/tests/conftest.py +++ b/spyder/app/tests/conftest.py @@ -27,7 +27,6 @@ from spyder.app import start from spyder.config.base import get_home_dir, running_in_ci from spyder.config.manager import CONF -from spyder.plugins.debugger.api import DebuggerToolbarActions from spyder.plugins.ipythonconsole.utils.kernelspec import SpyderKernelSpec from spyder.plugins.projects.api import EmptyProject from spyder.plugins.run.api import RunActions, StoredRunConfigurationExecutor @@ -395,7 +394,7 @@ def main_window(request, tmpdir, qtbot): toolbar = window.get_plugin(Plugins.Toolbar) debug_toolbar = toolbar.get_application_toolbar(ApplicationToolbars.Debug) debug_action = window.debugger.get_action( - DebuggerToolbarActions.DebugCurrentFile) + "run file in debugger") debug_button = debug_toolbar.widgetForAction(debug_action) window.debug_button = debug_button diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index 3f750cbfe2b..60099d1cf89 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -56,7 +56,7 @@ from spyder.config.manager import CONF from spyder.dependencies import DEPENDENCIES from spyder.plugins.debugger.api import ( - DebuggerWidgetActions, DebuggerToolbarActions) + DebuggerWidgetActions) from spyder.plugins.externalconsole.api import ExtConsoleShConfiguration from spyder.plugins.help.widgets import ObjectComboBox from spyder.plugins.help.tests.test_plugin import check_text @@ -640,7 +640,7 @@ def test_move_to_first_breakpoint(main_window, qtbot, debugcell): # Debug the cell debug_cell_action = main_window.debugger.get_action( - DebuggerToolbarActions.DebugCurrentCell) + "run cell in debugger") with qtbot.waitSignal(shell.executed): debug_cell_action.trigger() @@ -1568,7 +1568,7 @@ def test_run_code(main_window, qtbot, tmpdir): # ---- Debug cell ------ debug_cell_action = main_window.debugger.get_action( - DebuggerToolbarActions.DebugCurrentCell) + "run cell in debugger") with qtbot.waitSignal(shell.executed): debug_cell_action.trigger() qtbot.keyClicks(shell._control, '!c') @@ -5957,7 +5957,7 @@ def test_debug_selection(main_window, qtbot): control = shell._control debug_widget = main_window.debugger.get_widget() debug_selection_action = debug_widget.get_action( - DebuggerToolbarActions.DebugCurrentSelection) + "run selection in debugger") continue_action = debug_widget.get_action( DebuggerWidgetActions.Continue) From a1c7100f3932d24f7964fac4bb7551424d77409d Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Sun, 19 Feb 2023 19:20:12 +0100 Subject: [PATCH 10/19] fix tests --- spyder/app/tests/conftest.py | 2 +- spyder/app/tests/test_mainwindow.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spyder/app/tests/conftest.py b/spyder/app/tests/conftest.py index 44fc12a30fb..580a975cff2 100755 --- a/spyder/app/tests/conftest.py +++ b/spyder/app/tests/conftest.py @@ -393,7 +393,7 @@ def main_window(request, tmpdir, qtbot): # it's used a lot. toolbar = window.get_plugin(Plugins.Toolbar) debug_toolbar = toolbar.get_application_toolbar(ApplicationToolbars.Debug) - debug_action = window.debugger.get_action( + debug_action = window.run.get_action( "run file in debugger") debug_button = debug_toolbar.widgetForAction(debug_action) window.debug_button = debug_button diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index 60099d1cf89..f37b51352fa 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -639,7 +639,7 @@ def test_move_to_first_breakpoint(main_window, qtbot, debugcell): Qt.LeftButton) # Debug the cell - debug_cell_action = main_window.debugger.get_action( + debug_cell_action = main_window.run.get_action( "run cell in debugger") with qtbot.waitSignal(shell.executed): debug_cell_action.trigger() @@ -1567,7 +1567,7 @@ def test_run_code(main_window, qtbot, tmpdir): reset_run_code(qtbot, shell, code_editor, nsb) # ---- Debug cell ------ - debug_cell_action = main_window.debugger.get_action( + debug_cell_action = main_window.run.get_action( "run cell in debugger") with qtbot.waitSignal(shell.executed): debug_cell_action.trigger() @@ -5956,7 +5956,7 @@ def test_debug_selection(main_window, qtbot): shell = main_window.ipyconsole.get_current_shellwidget() control = shell._control debug_widget = main_window.debugger.get_widget() - debug_selection_action = debug_widget.get_action( + debug_selection_action = main_window.run.get_action( "run selection in debugger") continue_action = debug_widget.get_action( DebuggerWidgetActions.Continue) From 41b2da050072abe9e5ef2c85f4de169f86554faa Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Sun, 19 Feb 2023 19:21:44 +0100 Subject: [PATCH 11/19] revert changes --- spyder/plugins/debugger/plugin.py | 16 ---------------- spyder/plugins/debugger/widgets/main_widget.py | 16 +++------------- 2 files changed, 3 insertions(+), 29 deletions(-) diff --git a/spyder/plugins/debugger/plugin.py b/spyder/plugins/debugger/plugin.py index 30ce6dec771..13148ce28aa 100644 --- a/spyder/plugins/debugger/plugin.py +++ b/spyder/plugins/debugger/plugin.py @@ -181,22 +181,6 @@ def on_run_available(self): add_to_toolbar=ApplicationToolbars.Debug ) - widget = self.get_widget() - main_toolbar = widget.get_main_toolbar() - - for item in [ - debug_file_action, - debug_cell_action, - debug_selection_action, - ]: - widget.add_item_to_toolbar( - item, - toolbar=main_toolbar, - section=DebuggerWidgetMainToolBarSections.Run, - before_section=DebuggerWidgetMainToolBarSections.Main, - ) - main_toolbar._render() - @on_plugin_teardown(plugin=Plugins.Run) def on_run_teardown(self): run = self.get_plugin(Plugins.Run) diff --git a/spyder/plugins/debugger/widgets/main_widget.py b/spyder/plugins/debugger/widgets/main_widget.py index ccfb744e3c3..3938b8cf69c 100644 --- a/spyder/plugins/debugger/widgets/main_widget.py +++ b/spyder/plugins/debugger/widgets/main_widget.py @@ -307,28 +307,18 @@ def setup(self): # Main toolbar main_toolbar = self.get_main_toolbar() - secondary_toolbar = self.create_toolbar("widget_control") - - for item in [ - enter_debug_action, - inspect_action, - ]: - self.add_item_to_toolbar( - item, - toolbar=main_toolbar, - section=DebuggerWidgetMainToolBarSections.Main, - ) - for item in [next_action, continue_action, step_action, return_action, stop_action, goto_cursor_action, + enter_debug_action, + inspect_action, search_action]: self.add_item_to_toolbar( item, - toolbar=secondary_toolbar, + toolbar=main_toolbar, section=DebuggerWidgetMainToolBarSections.Main, ) From 6cf9d54e4a5fbc88e3bc00721ee6e35bc37db105 Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Sun, 19 Feb 2023 19:48:20 +0100 Subject: [PATCH 12/19] fix test --- spyder/app/tests/test_mainwindow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index f37b51352fa..0b0859790fe 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -1819,8 +1819,8 @@ def get_random_plugin(): # editor by error debugger = main_window.debugger debug_next_action = debugger.get_action(DebuggerWidgetActions.Next) - debug_next_button = debugger.get_widget()._auxiliary_toolbars[ - "widget_control"].widgetForAction(debug_next_action) + debug_next_button = debugger.get_widget()._main_toolbar.widgetForAction( + debug_next_action) with qtbot.waitSignal(shell.executed): qtbot.mouseClick(debug_next_button, Qt.LeftButton) assert not main_window.editor._ismaximized From 9db27ebb328afd0a342c431117a4f063c3c5ace0 Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Mon, 20 Feb 2023 04:31:56 +0100 Subject: [PATCH 13/19] Apply suggestions from code review Co-authored-by: Carlos Cordoba --- spyder/app/tests/test_mainwindow.py | 3 +-- spyder/config/main.py | 6 +++--- spyder/plugins/console/widgets/main_widget.py | 3 +-- spyder/plugins/debugger/plugin.py | 15 +++++++-------- .../plugins/debugger/widgets/main_widget.py | 3 +-- spyder/plugins/editor/api/run.py | 2 +- spyder/plugins/editor/plugin.py | 19 ++++++++++--------- spyder/plugins/editor/widgets/editor.py | 1 - spyder/plugins/ipythonconsole/api.py | 3 ++- spyder/plugins/ipythonconsole/plugin.py | 8 ++------ .../ipythonconsole/widgets/main_widget.py | 7 +++---- spyder/plugins/run/api.py | 4 ++-- spyder/plugins/run/container.py | 5 +++-- spyder/plugins/run/plugin.py | 14 +++++++++----- 14 files changed, 45 insertions(+), 48 deletions(-) diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index 0b0859790fe..471a6082e37 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -55,8 +55,7 @@ get_home_dir, get_conf_path, get_module_path, running_in_ci) from spyder.config.manager import CONF from spyder.dependencies import DEPENDENCIES -from spyder.plugins.debugger.api import ( - DebuggerWidgetActions) +from spyder.plugins.debugger.api import DebuggerWidgetActions from spyder.plugins.externalconsole.api import ExtConsoleShConfiguration from spyder.plugins.help.widgets import ObjectComboBox from spyder.plugins.help.tests.test_plugin import check_text diff --git a/spyder/config/main.py b/spyder/config/main.py index 168cd65abe8..46a7c93e398 100644 --- a/spyder/config/main.py +++ b/spyder/config/main.py @@ -392,7 +392,6 @@ '_/file switcher': 'Ctrl+P', '_/symbol finder': 'Ctrl+Alt+P', '_/run': "F5", - '_/run selection': "F9", '_/configure': "Ctrl+F6", '_/re-run last script': "F6", # -- In plugins/init @@ -460,8 +459,6 @@ 'editor/select all': "Ctrl+A", # -- In widgets/editor.py 'editor/inspect current object': 'Ctrl+I', - 'editor/run selection up to line': 'Shift+F9', - 'editor/run selection from line': CTRL + '+F9', 'editor/go to line': 'Ctrl+L', 'editor/go to previous file': CTRL + '+Shift+Tab', 'editor/go to next file': CTRL + '+Tab', @@ -487,6 +484,9 @@ 'editor/close file 2': "Ctrl+F4", 'editor/run cell': CTRL + '+Return', 'editor/run cell and advance': 'Shift+Return', + 'editor/run selection and advance': "F9", + 'editor/run selection up to line': 'Shift+F9', + 'editor/run selection from line': CTRL + '+F9', 'editor/go to next cell': 'Ctrl+Down', 'editor/go to previous cell': 'Ctrl+Up', 'editor/re-run cell': 'Alt+Return', diff --git a/spyder/plugins/console/widgets/main_widget.py b/spyder/plugins/console/widgets/main_widget.py index 00b3be8e1e3..0a0c5b9dd82 100644 --- a/spyder/plugins/console/widgets/main_widget.py +++ b/spyder/plugins/console/widgets/main_widget.py @@ -496,8 +496,7 @@ def show_syspath(self): self.dialog_manager.show(editor) @Slot() - def run_script(self, filename=None, silent=False, - args=None): + def run_script(self, filename=None, silent=False, args=None): """ Run a Python script. """ diff --git a/spyder/plugins/debugger/plugin.py b/spyder/plugins/debugger/plugin.py index 13148ce28aa..be0b239cb2e 100644 --- a/spyder/plugins/debugger/plugin.py +++ b/spyder/plugins/debugger/plugin.py @@ -24,8 +24,7 @@ from spyder.plugins.debugger.utils.breakpointsmanager import ( BreakpointsManager, clear_all_breakpoints, clear_breakpoint) from spyder.plugins.debugger.widgets.main_widget import ( - DebuggerBreakpointActions, DebuggerWidget, - DebuggerWidgetActions, DebuggerWidgetMainToolBarSections) + DebuggerBreakpointActions, DebuggerWidget, DebuggerWidgetActions) from spyder.plugins.editor.utils.editor import get_file_language from spyder.plugins.editor.utils.languages import ALL_LANGUAGES from spyder.plugins.ipythonconsole.api import IPythonConsolePyConfiguration @@ -133,7 +132,7 @@ def on_run_available(self): run = self.get_plugin(Plugins.Run) run.register_executor_configuration(self, self.executor_configuration) - debug_file_action = run.create_run_in_executor_button( + run.create_run_in_executor_button( RunContext.File, self.NAME, text=_("&Debug file"), @@ -149,7 +148,7 @@ def on_run_available(self): add_to_toolbar=ApplicationToolbars.Debug ) - debug_cell_action = run.create_run_in_executor_button( + run.create_run_in_executor_button( RunContext.Cell, self.NAME, text=_("Debug cell"), @@ -165,7 +164,7 @@ def on_run_available(self): add_to_toolbar=ApplicationToolbars.Debug ) - debug_selection_action = run.create_run_in_executor_button( + run.create_run_in_executor_button( RunContext.Selection, self.NAME, text=_("Debug selection or current line"), @@ -512,7 +511,7 @@ def can_close_file(self, filename=None): # ---- For execution @run_execute(context=RunContext.File) - def exec_files( + def debug_file( self, input: RunConfiguration, conf: ExtendedRunExecutionParameters @@ -531,7 +530,7 @@ def exec_files( self.get_widget().set_pdb_take_focus(False) @run_execute(context=RunContext.Cell) - def exec_cell( + def debug_cell( self, input: RunConfiguration, conf: ExtendedRunExecutionParameters @@ -556,7 +555,7 @@ def exec_cell( @run_execute(context=RunContext.Selection) - def exec_selection( + def debug_selection( self, input: RunConfiguration, conf: ExtendedRunExecutionParameters diff --git a/spyder/plugins/debugger/widgets/main_widget.py b/spyder/plugins/debugger/widgets/main_widget.py index 3938b8cf69c..2ac9096afd2 100644 --- a/spyder/plugins/debugger/widgets/main_widget.py +++ b/spyder/plugins/debugger/widgets/main_widget.py @@ -56,7 +56,6 @@ class DebuggerWidgetOptionsMenuSections: class DebuggerWidgetMainToolBarSections: - Run = "run" Main = 'main_section' @@ -490,7 +489,7 @@ def print_debug_file_msg(self): def set_pdb_take_focus(self, take_focus): """ - Set whether current pdb should take focus when stopping on the + Set whether current Pdb session should take focus when stopping on the next call. """ widget = self.current_widget() diff --git a/spyder/plugins/editor/api/run.py b/spyder/plugins/editor/api/run.py index fdcca6a5cef..cc4e99068be 100644 --- a/spyder/plugins/editor/api/run.py +++ b/spyder/plugins/editor/api/run.py @@ -88,4 +88,4 @@ class SelectionContextModificator: class ExtraAction: - Advance = "advance" \ No newline at end of file + Advance = "advance" diff --git a/spyder/plugins/editor/plugin.py b/spyder/plugins/editor/plugin.py index 01e80a05e7e..60e25503dc7 100644 --- a/spyder/plugins/editor/plugin.py +++ b/spyder/plugins/editor/plugin.py @@ -283,7 +283,7 @@ def __init__(self, parent, ignore_last_opened_files=False): _("Run cell"), icon=ima.icon('run_cell'), tip=_("Run current cell\n[Use #%% to create cells]"), - shortcut_context="editor", + shortcut_context=self.NAME, register_shortcut=True, add_to_toolbar=True, add_to_menu=True @@ -294,7 +294,7 @@ def __init__(self, parent, ignore_last_opened_files=False): _("Run cell and advance"), icon=ima.icon('run_cell_advance'), tip=_("Run current cell and go to the next one"), - shortcut_context="editor", + shortcut_context=self.NAME, register_shortcut=True, add_to_toolbar=True, add_to_menu=True, @@ -305,7 +305,7 @@ def __init__(self, parent, ignore_last_opened_files=False): RunContext.Cell, _("Re-run last cell"), tip=_("Re run last cell "), - shortcut_context="editor", + shortcut_context=self.NAME, register_shortcut=True, add_to_menu=True, re_run=True @@ -316,7 +316,7 @@ def __init__(self, parent, ignore_last_opened_files=False): _("Run &selection or current line"), icon=ima.icon('run_selection'), tip=_("Run selection or current line"), - shortcut_context="_", + shortcut_context=self.NAME, register_shortcut=True, add_to_toolbar=True, add_to_menu=True, @@ -327,7 +327,7 @@ def __init__(self, parent, ignore_last_opened_files=False): RunContext.Selection, _("Run &to line"), tip=_("Run selection up to the current line"), - shortcut_context="editor", + shortcut_context=self.NAME, register_shortcut=True, add_to_toolbar=False, add_to_menu=True, @@ -338,7 +338,7 @@ def __init__(self, parent, ignore_last_opened_files=False): RunContext.Selection, _("Run &from line"), tip=_("Run selection from the current line"), - shortcut_context="editor", + shortcut_context=self.NAME, register_shortcut=True, add_to_toolbar=False, add_to_menu=True, @@ -2951,9 +2951,9 @@ def get_run_configuration_per_context( if context == RunContext.Selection: if context_modificator == SelectionContextModificator.ToLine: - ret = editorstack.get_to_current_line() - if ret is not None: - text, offsets, line_cols, enc = ret + to_current_line = editorstack.get_to_current_line() + if to_current_line is not None: + text, offsets, line_cols, enc = to_current_line else: return elif ( @@ -2963,6 +2963,7 @@ def get_run_configuration_per_context( editorstack.get_from_current_line()) else: text, offsets, line_cols, enc = editorstack.get_selection() + if extra_action_name == ExtraAction.Advance: editorstack.advance_line() context_name = 'Selection' diff --git a/spyder/plugins/editor/widgets/editor.py b/spyder/plugins/editor/widgets/editor.py index ddb623f406f..e85da001e51 100644 --- a/spyder/plugins/editor/widgets/editor.py +++ b/spyder/plugins/editor/widgets/editor.py @@ -2861,7 +2861,6 @@ def advance_line(self): editor.move_cursor_to_next('line', 'down') - def get_current_cell(self): """Get current cell attributes.""" text, block, off_pos, line_col_pos = ( diff --git a/spyder/plugins/ipythonconsole/api.py b/spyder/plugins/ipythonconsole/api.py index c19db1c61dd..3983ea8b45c 100644 --- a/spyder/plugins/ipythonconsole/api.py +++ b/spyder/plugins/ipythonconsole/api.py @@ -38,5 +38,6 @@ class IPythonConsolePyConfiguration(TypedDict): # then it will use an empty one. console_namespace: bool - # If not None, then the console will use an alternative run method. + # If not None, then the console will use an alternative run method + # (e.g. `runfile`, `debugfile` or `debugcell`). run_method: NotRequired[str] diff --git a/spyder/plugins/ipythonconsole/plugin.py b/spyder/plugins/ipythonconsole/plugin.py index a4dcaf30705..0467970ae8f 100644 --- a/spyder/plugins/ipythonconsole/plugin.py +++ b/spyder/plugins/ipythonconsole/plugin.py @@ -712,8 +712,7 @@ def exec_cell( def run_script(self, filename, wdir, args='', post_mortem=False, current_client=True, clear_variables=False, console_namespace=False, - method=None, - force_wdir=False): + method=None, force_wdir=False): """ Run script in current or dedicated client. @@ -775,16 +774,13 @@ def run_cell(self, code, cell_name, filename, method : str, optional Name handler of the kernel function to be used to execute the cell. The default is 'runcell'. - set_focus: bool - Whether to give focus to the console after running the cell. Returns ------- None. """ self.sig_unmaximize_plugin_requested.emit() - self.get_widget().run_cell( - code, cell_name, filename, method=method) + self.get_widget().run_cell(code, cell_name, filename, method=method) def execute_code(self, lines, current_client=True, clear_variables=False): """ diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index ea7fc403ef0..6db05b7ce89 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -1823,8 +1823,7 @@ def interrupt_kernel(self): client.stop_button_click_handler() # ---- For cells - def run_cell(self, code, cell_name, filename, - method='runcell'): + def run_cell(self, code, cell_name, filename, method='runcell'): """Run cell in current or dedicated client.""" def norm(text): @@ -1878,8 +1877,8 @@ def norm(text): # ---- For scripts def run_script(self, filename, wdir, args, post_mortem, current_client, - clear_variables, console_namespace, - method=None, force_wdir=False): + clear_variables, console_namespace, method=None, + force_wdir=False): """Run script in current or dedicated client.""" norm = lambda text: remove_backslashes(str(text)) diff --git a/spyder/plugins/run/api.py b/spyder/plugins/run/api.py index f467313e9ec..bd258dac0d5 100644 --- a/spyder/plugins/run/api.py +++ b/spyder/plugins/run/api.py @@ -329,8 +329,8 @@ def get_run_configuration_per_context( after gathering the run configuration input. Else, no action needs to take place. context_modificator: Optional[str] - str describing how to alter the context. - e.g. run selection + str describing how to alter the context, e.g. run selection + . re_run: bool If True, then the requested configuration should correspond to the last executed configuration for the given context. diff --git a/spyder/plugins/run/container.py b/spyder/plugins/run/container.py index e64c7ef7e39..25acfccc086 100644 --- a/spyder/plugins/run/container.py +++ b/spyder/plugins/run/container.py @@ -400,8 +400,8 @@ def create_run_button( The name of the action to execute on the run input provider after requesting the run input. context_modificator: Optional[str] - The name of the modification to apply to the action. - e.g. run selection + The name of the modification to apply to the action, e.g. run + selection . re_run: bool If True, then the button will act as a re-run button instead of a run one. @@ -432,6 +432,7 @@ def create_run_button( shortcuts. """ dict_actions = self.re_run_actions if re_run else self.context_actions + if ( (context_name, extra_action_name, context_modificator) in dict_actions diff --git a/spyder/plugins/run/plugin.py b/spyder/plugins/run/plugin.py index 1e9d00d79dd..9afd0289331 100644 --- a/spyder/plugins/run/plugin.py +++ b/spyder/plugins/run/plugin.py @@ -425,11 +425,11 @@ def create_run_button( The name of the action to execute on the run input provider after requesting the run input. context_modificator: Optional[str] - The name of the modification to apply to the action. - e.g. run selection + The name of the modification to apply to the action, e.g. run + selection . add_to_toolbar: object If True, then the action will be added to the Run section of the - main toolbar. If a string, it will be a toolbat id + main toolbar. If a string, it must be a toolbar_id add_to_menu: object If True, then the action will be added to the Run menu. If a tuple of 3 strings, it corresponds to @@ -482,6 +482,7 @@ def create_run_button( toolbar_id = ApplicationToolbars.Run if isinstance(add_to_toolbar, str): toolbar_id = add_to_toolbar + toolbar = self.get_plugin(Plugins.Toolbar) if toolbar: toolbar.add_item_to_application_toolbar( @@ -498,6 +499,7 @@ def create_run_button( ) if isinstance(add_to_menu, tuple): menu_id, menu_section, before_section = add_to_menu + main_menu = self.get_plugin(Plugins.MainMenu) if main_menu: main_menu.add_item_to_application_menu( @@ -543,8 +545,8 @@ def destroy_run_button( The name of the action to execute on the run input provider after requesting the run input. context_modificator: Optional[str] - The name of the modification to apply to the action. - e.g. run selection + The name of the modification to apply to the action, e.g. run + run selection . re_run: bool If True, then the button was registered as a re-run button instead of a run one. @@ -663,6 +665,7 @@ def create_run_in_executor_button( toolbar_id = ApplicationToolbars.Run if isinstance(add_to_toolbar, str): toolbar_id = add_to_toolbar + toolbar = self.get_plugin(Plugins.Toolbar) if toolbar: toolbar.add_item_to_application_toolbar( @@ -679,6 +682,7 @@ def create_run_in_executor_button( ) if isinstance(add_to_menu, tuple): menu_id, menu_section, before_section = add_to_menu + main_menu = self.get_plugin(Plugins.MainMenu) if main_menu: main_menu.add_item_to_application_menu( From 6d9394a87fea735ff9a6ddd1ebcf8f48687581dd Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Mon, 20 Feb 2023 04:32:58 +0100 Subject: [PATCH 14/19] Update spyder/plugins/ipythonconsole/plugin.py Co-authored-by: Carlos Cordoba --- spyder/plugins/ipythonconsole/plugin.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spyder/plugins/ipythonconsole/plugin.py b/spyder/plugins/ipythonconsole/plugin.py index 0467970ae8f..c0cffc19c74 100644 --- a/spyder/plugins/ipythonconsole/plugin.py +++ b/spyder/plugins/ipythonconsole/plugin.py @@ -758,8 +758,7 @@ def run_script(self, filename, wdir, args='', method, force_wdir) - def run_cell(self, code, cell_name, filename, - method='runcell'): + def run_cell(self, code, cell_name, filename, method='runcell'): """ Run cell in current or dedicated client. From 3d38d30fc7c615ab83dee1488ea0006db1b8d154 Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Mon, 20 Feb 2023 04:44:16 +0100 Subject: [PATCH 15/19] update add_to_menu option --- spyder/plugins/debugger/plugin.py | 30 +++++++++++++++--------------- spyder/plugins/run/plugin.py | 20 ++++++++++++-------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/spyder/plugins/debugger/plugin.py b/spyder/plugins/debugger/plugin.py index be0b239cb2e..78513cc442e 100644 --- a/spyder/plugins/debugger/plugin.py +++ b/spyder/plugins/debugger/plugin.py @@ -140,11 +140,11 @@ def on_run_available(self): icon=self.create_icon('debug'), shortcut_context=self.NAME, register_shortcut=True, - add_to_menu=( - ApplicationMenus.Debug, - DebugMenuSections.StartDebug, - DebugMenuSections.ControlDebug - ), + add_to_menu={ + "menu": ApplicationMenus.Debug, + "section": DebugMenuSections.StartDebug, + "before_section": DebugMenuSections.ControlDebug + }, add_to_toolbar=ApplicationToolbars.Debug ) @@ -156,11 +156,11 @@ def on_run_available(self): icon=self.create_icon('debug_cell'), shortcut_context=self.NAME, register_shortcut=True, - add_to_menu=( - ApplicationMenus.Debug, - DebugMenuSections.StartDebug, - DebugMenuSections.ControlDebug - ), + add_to_menu={ + "menu": ApplicationMenus.Debug, + "section": DebugMenuSections.StartDebug, + "before_section": DebugMenuSections.ControlDebug + }, add_to_toolbar=ApplicationToolbars.Debug ) @@ -172,11 +172,11 @@ def on_run_available(self): icon=self.create_icon('debug_selection'), shortcut_context=self.NAME, register_shortcut=True, - add_to_menu=( - ApplicationMenus.Debug, - DebugMenuSections.StartDebug, - DebugMenuSections.ControlDebug - ), + add_to_menu={ + "menu": ApplicationMenus.Debug, + "section": DebugMenuSections.StartDebug, + "before_section": DebugMenuSections.ControlDebug + }, add_to_toolbar=ApplicationToolbars.Debug ) diff --git a/spyder/plugins/run/plugin.py b/spyder/plugins/run/plugin.py index 9afd0289331..2fce17b6439 100644 --- a/spyder/plugins/run/plugin.py +++ b/spyder/plugins/run/plugin.py @@ -432,8 +432,8 @@ def create_run_button( main toolbar. If a string, it must be a toolbar_id add_to_menu: object If True, then the action will be added to the Run menu. - If a tuple of 3 strings, it corresponds to - (menu_id, menu_section, before_section) + If a dictionnary, it corresponds to + {'menu': ..., 'section': ..., 'before_section': ...} re_run: bool If True, then the button will act as a re-run button instead of a run one. @@ -497,8 +497,10 @@ def create_run_button( ApplicationMenus.Run, RunMenuSections.RunExtras, RunMenuSections.RunInExecutors ) - if isinstance(add_to_menu, tuple): - menu_id, menu_section, before_section = add_to_menu + if isinstance(add_to_menu, dict): + menu_id = add_to_menu['menu'] + menu_section = add_to_menu['section'] + before_section = add_to_menu.get('before_section', None) main_menu = self.get_plugin(Plugins.MainMenu) if main_menu: @@ -624,8 +626,8 @@ def create_run_in_executor_button( main toolbar. If a string, it will be a toolbat id add_to_menu: object If True, then the action will be added to the Run menu. - If a tuple of 3 strings, it corresponds to - (menu_id, menu_section, before_section) + If a dictionnary, it corresponds to + {'menu': ..., 'section': ..., 'before_section': ...} Returns ------- @@ -680,8 +682,10 @@ def create_run_in_executor_button( ApplicationMenus.Run, RunMenuSections.RunExtras, RunMenuSections.RunInExecutors ) - if isinstance(add_to_menu, tuple): - menu_id, menu_section, before_section = add_to_menu + if isinstance(add_to_menu, dict): + menu_id = add_to_menu['menu'] + menu_section = add_to_menu['section'] + before_section = add_to_menu.get('before_section', None) main_menu = self.get_plugin(Plugins.MainMenu) if main_menu: From 36f50aab232a76c1ceb337624996dda54fc3c7d4 Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Mon, 20 Feb 2023 04:50:45 +0100 Subject: [PATCH 16/19] update run config --- spyder/plugins/run/confpage.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/spyder/plugins/run/confpage.py b/spyder/plugins/run/confpage.py index 0e19220d0cf..ac9de06ee06 100644 --- a/spyder/plugins/run/confpage.py +++ b/spyder/plugins/run/confpage.py @@ -16,7 +16,7 @@ from qtpy.QtCore import Qt from qtpy.QtWidgets import (QGroupBox, QLabel, QVBoxLayout, QComboBox, QTableView, QAbstractItemView, QPushButton, - QGridLayout, QHeaderView) + QGridLayout, QHeaderView, QTabWidget, QWidget) # Local imports from spyder.api.preferences import PluginConfigPage @@ -233,18 +233,28 @@ def setup_page(self): run_layout = QVBoxLayout() run_layout.addWidget(saveall_box) run_layout.addWidget(run_cell_box) + run_widget = QWidget() + run_widget.setLayout(run_layout) - editor_group = QGroupBox(_("Editor interaction")) - editor_group.setLayout(run_layout) - vlayout = QVBoxLayout(self) - vlayout.addWidget(editor_group) + + vlayout = QVBoxLayout() vlayout.addWidget(about_label) vlayout.addSpacing(10) vlayout.addWidget(executor_group) vlayout.addWidget(params_group) vlayout.addLayout(sn_buttons_layout) vlayout.addStretch(1) + executor_widget = QWidget() + executor_widget.setLayout(vlayout) + + self.tabs = QTabWidget() + self.tabs.addTab(self.create_tab(executor_widget), _("Run executors")) + self.tabs.addTab( + self.create_tab(run_widget), _("Editor interaction")) + main_layout = QVBoxLayout() + main_layout.addWidget(self.tabs) + self.setLayout(main_layout) def executor_index_changed(self, index: int): # Save previous executor configuration From be0a91b6387efa4e5d9ca3be2d2d93ed85e326c7 Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Mon, 20 Feb 2023 04:59:41 +0100 Subject: [PATCH 17/19] remove force_wdir --- spyder/plugins/ipythonconsole/plugin.py | 15 +++------------ .../plugins/ipythonconsole/widgets/main_widget.py | 9 +-------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/spyder/plugins/ipythonconsole/plugin.py b/spyder/plugins/ipythonconsole/plugin.py index c0cffc19c74..a336f19d23a 100644 --- a/spyder/plugins/ipythonconsole/plugin.py +++ b/spyder/plugins/ipythonconsole/plugin.py @@ -655,11 +655,6 @@ def exec_files( # Fixes spyder-ide/spyder#2158. filename = filename.replace("'", r"\'").replace('"', r'\"') - force_wdir = False - if wdir is not None: - if osp.isdir(wdir): - force_wdir = True - self.run_script( filename, wdir, @@ -669,7 +664,6 @@ def exec_files( clear_variables, console_namespace, method=run_method, - force_wdir=force_wdir ) return [] @@ -712,7 +706,7 @@ def exec_cell( def run_script(self, filename, wdir, args='', post_mortem=False, current_client=True, clear_variables=False, console_namespace=False, - method=None, force_wdir=False): + method=None): """ Run script in current or dedicated client. @@ -738,9 +732,6 @@ def run_script(self, filename, wdir, args='', method : str or None Method to run the file. It must accept the same arguments as `runfile`. - force_wdir: bool - The working directory is ignored on remote kernels except if - force_wdir is True Returns ------- @@ -755,8 +746,8 @@ def run_script(self, filename, wdir, args='', current_client, clear_variables, console_namespace, - method, - force_wdir) + method + ) def run_cell(self, code, cell_name, filename, method='runcell'): """ diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index 6db05b7ce89..a50839611a8 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -1877,8 +1877,7 @@ def norm(text): # ---- For scripts def run_script(self, filename, wdir, args, post_mortem, current_client, - clear_variables, console_namespace, method=None, - force_wdir=False): + clear_variables, console_namespace, method=None): """Run script in current or dedicated client.""" norm = lambda text: remove_backslashes(str(text)) @@ -1906,12 +1905,6 @@ def run_script(self, filename, wdir, args, post_mortem, current_client, line = method + "('%s'" % (norm(filename)) if args: line += ", args='%s'" % norm(args) - if ( - wdir and client.shellwidget.is_external_kernel - and not force_wdir - ): - # No working directory for external kernels - wdir = '' if wdir: line += ", wdir='%s'" % norm(wdir) if post_mortem: From 1b61fffe18ba336c4a4f9750171e121e2223fde5 Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Mon, 20 Feb 2023 20:46:51 +0100 Subject: [PATCH 18/19] fix external kernels --- spyder/plugins/ipythonconsole/widgets/main_widget.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index 18c9755f2f4..e14c87bf1fe 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -1903,6 +1903,12 @@ def run_script(self, filename, wdir, args, post_mortem, current_client, line = method + "('%s'" % (norm(filename)) if args: line += ", args='%s'" % norm(args) + if ( + wdir and client.shellwidget.is_external_kernel + and os.path.samefile(wdir, os.path.dirname(filename)) + ): + # No working directory for external kernels + wdir = "" if wdir: line += ", wdir='%s'" % norm(wdir) if post_mortem: From d9440eb7603e9e39ab5ef2df77ca922eb0c8d145 Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Mon, 20 Feb 2023 20:50:22 +0100 Subject: [PATCH 19/19] update conf --- spyder/plugins/run/confpage.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/spyder/plugins/run/confpage.py b/spyder/plugins/run/confpage.py index ac9de06ee06..2699db7e418 100644 --- a/spyder/plugins/run/confpage.py +++ b/spyder/plugins/run/confpage.py @@ -178,11 +178,6 @@ def setup_page(self): self.executor_index_changed) self.executor_combo.setModel(self.executor_model) - executor_group = QGroupBox(_('Run executors')) - executor_layout = QVBoxLayout() - executor_layout.addWidget(self.executor_combo) - executor_group.setLayout(executor_layout) - self.params_table = RunParametersTableView(self, self.table_model) self.params_table.setMaximumHeight(180) @@ -223,7 +218,7 @@ def setup_page(self): sn_buttons_layout.setColumnStretch(1, 2) sn_buttons_layout.setColumnStretch(2, 1) - # --- Run code tab --- + # --- Editor interactions tab --- newcb = self.create_checkbox saveall_box = newcb(_("Save all files before running script"), 'save_all_before_run') @@ -236,12 +231,10 @@ def setup_page(self): run_widget = QWidget() run_widget.setLayout(run_layout) - - vlayout = QVBoxLayout() vlayout.addWidget(about_label) vlayout.addSpacing(10) - vlayout.addWidget(executor_group) + vlayout.addWidget(self.executor_combo) vlayout.addWidget(params_group) vlayout.addLayout(sn_buttons_layout) vlayout.addStretch(1) @@ -251,7 +244,7 @@ def setup_page(self): self.tabs = QTabWidget() self.tabs.addTab(self.create_tab(executor_widget), _("Run executors")) self.tabs.addTab( - self.create_tab(run_widget), _("Editor interaction")) + self.create_tab(run_widget), _("Editor interactions")) main_layout = QVBoxLayout() main_layout.addWidget(self.tabs) self.setLayout(main_layout)