diff --git a/spyder/config/main.py b/spyder/config/main.py index 1bab199967f..1dffc004c25 100755 --- a/spyder/config/main.py +++ b/spyder/config/main.py @@ -390,8 +390,8 @@ 'editor/rotate kill ring': 'Shift+Meta+Y', 'editor/kill previous word': 'Meta+Backspace', 'editor/kill next word': 'Meta+D', - 'editor/start of document': 'Ctrl+Up', - 'editor/end of document': 'Ctrl+Down', + 'editor/start of document': 'Ctrl+Home', + 'editor/end of document': 'Ctrl+End', 'editor/undo': 'Ctrl+Z', 'editor/redo': 'Ctrl+Shift+Z', 'editor/cut': 'Ctrl+X', @@ -425,6 +425,8 @@ 'editor/close file 2': "Ctrl+F4", 'editor/run cell': RUN_CELL_SHORTCUT, 'editor/run cell and advance': RUN_CELL_AND_ADVANCE_SHORTCUT, + 'editor/go to next cell': 'Ctrl+Down', + 'editor/go to previous cell': 'Ctrl+Up', 'editor/re-run last cell': RE_RUN_LAST_CELL_SHORTCUT, # -- In plugins/editor.py 'editor/show/hide outline': "Ctrl+Alt+O", @@ -658,7 +660,7 @@ # or if you want to *rename* options, then you need to do a MAJOR update in # version, e.g. from 3.0.0 to 4.0.0 # 3. You don't need to touch this value if you're just adding a new option -CONF_VERSION = '33.1.0' +CONF_VERSION = '33.2.0' # Main configuration instance try: diff --git a/spyder/widgets/editor.py b/spyder/widgets/editor.py index 39885a6597f..70903f0e285 100644 --- a/spyder/widgets/editor.py +++ b/spyder/widgets/editor.py @@ -627,6 +627,14 @@ def create_shortcuts(self): context="Editor", name="run cell and advance", parent=self) + go_to_next_cell = config_shortcut(self.advance_cell, + context="Editor", + name="go to next cell", + parent=self) + go_to_previous_cell = config_shortcut(lambda: self.advance_cell(reverse=True), + context="Editor", + name="go to previous cell", + parent=self) re_run_last_cell = config_shortcut(self.re_run_last_cell, context="Editor", name="re-run last cell", @@ -638,7 +646,7 @@ def create_shortcuts(self): save_all, save_as, close_all, prev_edit_pos, prev_cursor, next_cursor, zoom_in_1, zoom_in_2, zoom_out, zoom_reset, close_file_1, close_file_2, run_cell, run_cell_and_advance, - re_run_last_cell] + go_to_next_cell, go_to_previous_cell, re_run_last_cell] def get_shortcut_data(self): """ @@ -2031,14 +2039,26 @@ def run_cell(self): def run_cell_and_advance(self): """Run current cell and advance to the next one""" self.run_cell() + self.advance_cell() + + def advance_cell(self, reverse=False): + """Advance to the next cell. + + reverse = True --> go to previous cell. + """ + if not reverse: + move_func = self.get_current_editor().go_to_next_cell + else: + move_func = self.get_current_editor().go_to_previous_cell + if self.focus_to_editor: - self.get_current_editor().go_to_next_cell() + move_func() else: term = QApplication.focusWidget() - self.get_current_editor().go_to_next_cell() + move_func() term.setFocus() term = QApplication.focusWidget() - self.get_current_editor().go_to_next_cell() + move_func() term.setFocus() def re_run_last_cell(self): diff --git a/spyder/widgets/sourcecode/base.py b/spyder/widgets/sourcecode/base.py index a8c59fa55d6..1bc2db90e31 100644 --- a/spyder/widgets/sourcecode/base.py +++ b/spyder/widgets/sourcecode/base.py @@ -784,6 +784,32 @@ def go_to_next_cell(self): return self.setTextCursor(cursor) + def go_to_previous_cell(self): + """Go to the previous cell of lines""" + cursor = self.textCursor() + cur_pos = prev_pos = cursor.position() + + # Move to the begining of the cell + while not self.is_cell_separator(cursor): + # Moving to the previous code cell + cursor.movePosition(QTextCursor.PreviousBlock) + prev_pos = cur_pos + cur_pos = cursor.position() + if cur_pos == prev_pos: + return + + # Move to the previous cell + cursor.movePosition(QTextCursor.PreviousBlock) + cur_pos = prev_pos = cursor.position() + while not self.is_cell_separator(cursor): + # Moving to the previous code cell + cursor.movePosition(QTextCursor.PreviousBlock) + prev_pos = cur_pos + cur_pos = cursor.position() + if cur_pos == prev_pos: + return + self.setTextCursor(cursor) + def get_line_count(self): """Return document total line number""" return self.blockCount() diff --git a/spyder/widgets/tests/test_editor.py b/spyder/widgets/tests/test_editor.py index 99b60b88897..01370e5d9b9 100644 --- a/spyder/widgets/tests/test_editor.py +++ b/spyder/widgets/tests/test_editor.py @@ -62,6 +62,23 @@ def editor_find_replace_bot(base_editor_bot): qtbot.addWidget(editor_stack) return editor_stack, finfo.editor, find_replace, qtbot +@pytest.fixture +def editor_cells_bot(base_editor_bot): + editor_stack, qtbot = base_editor_bot + text = ('# %%\n' + '# 1 cell\n' + '# print(1)\n' + '# %%\n' + '# 2 cell\n' + '# print(2)\n' + '# %%\n' + '# 3 cell\n' + '# print(3)\n') + finfo = editor_stack.new('cells.py', 'utf-8', text) + find_replace = FindReplace(None, enable_replace=True) + qtbot.addWidget(editor_stack) + return editor_stack, finfo.editor, qtbot + # Tests #------------------------------- def test_run_top_line(editor_bot): @@ -138,6 +155,26 @@ def test_replace_current_selected_line(editor_find_replace_bot): qtbot.keyPress(finder.replace_text, Qt.Key_Return) assert editor.toPlainText()[0:-1] == expected_new_text +def test_advance_cell(editor_cells_bot): + editor_stack, editor, qtbot = editor_cells_bot + + # cursor at the end of the file + assert editor.get_cursor_line_column() == (10, 0) + + # advance backwards to 2nd cell + editor_stack.advance_cell(reverse=True) + assert editor.get_cursor_line_column() == (3, 0) + # advance backwards to 1st cell + editor_stack.advance_cell(reverse=True) + assert editor.get_cursor_line_column() == (0, 0) + + # advance to 2nd cell + editor_stack.advance_cell() + assert editor.get_cursor_line_column() == (3, 0) + # advance to 3rd cell + editor_stack.advance_cell() + assert editor.get_cursor_line_column() == (6, 0) + if __name__ == "__main__": pytest.main()