Skip to content

Commit

Permalink
Merge pull request #22480 from The-Ludwig/master
Browse files Browse the repository at this point in the history
PR: Copy/cut entire line if nothing is selected (Editor)
  • Loading branch information
ccordoba12 authored Nov 6, 2024
2 parents 631bb39 + 7605e2d commit a672bb7
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 1 deletion.
41 changes: 41 additions & 0 deletions spyder/plugins/editor/widgets/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,9 @@ def copy(self):
"""
if self.get_selected_text():
QApplication.clipboard().setText(self.get_selected_text())
else:
cursor = self.select_current_line_and_sep(set_cursor=False)
QApplication.clipboard().setText(self.get_selected_text(cursor))

def toPlainText(self):
"""
Expand Down Expand Up @@ -648,6 +651,44 @@ def select_current_cell(self, cursor=None):

return cursor, cell_full_file

def select_current_line_and_sep(self, cursor=None, set_cursor=True):
"""
Selects the current line, including the correct line separator to
delete or copy the whole current line.
This means:
- If there is a next block, select the current block's newline char.
- Else if there is a previous block, select the previous newline char.
- Else select no newline char (1-line file)
Does a similar thing to `cursor.select(QTextCursor.BlockUnderCursor)`,
which always selects the previous newline char.
"""
if cursor is None:
cursor = self.textCursor()

cursor.movePosition(QTextCursor.StartOfBlock, QTextCursor.MoveAnchor)

if not cursor.movePosition(QTextCursor.NextBlock,
QTextCursor.KeepAnchor):
# if there is no next Block, we select the previous newline
if cursor.movePosition(QTextCursor.PreviousBlock,
QTextCursor.MoveAnchor):
cursor.movePosition(QTextCursor.EndOfBlock,
QTextCursor.MoveAnchor)
cursor.movePosition(QTextCursor.NextBlock,
QTextCursor.KeepAnchor)
cursor.movePosition(QTextCursor.EndOfBlock,
QTextCursor.KeepAnchor)
else:
# if there is no previous block, we can select the current line
# this is the 1-line file case
cursor.select(QTextCursor.BlockUnderCursor)

if set_cursor:
self.setTextCursor(cursor)
return cursor

def go_to_next_cell(self):
"""Go to the next cell of lines"""
cursor = self.textCursor()
Expand Down
3 changes: 2 additions & 1 deletion spyder/plugins/editor/widgets/codeeditor/codeeditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1952,7 +1952,8 @@ def cut(self):
"""Reimplement cut to signal listeners about changes on the text."""
has_selected_text = self.has_selected_text()
if not has_selected_text:
return
self.select_current_line_and_sep()

start, end = self.get_selection_start_end()
self.sig_will_remove_selection.emit(start, end)
self.sig_delete_requested.emit()
Expand Down
66 changes: 66 additions & 0 deletions spyder/plugins/editor/widgets/codeeditor/tests/test_codeeditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

# Local imports
from spyder.widgets.mixins import TIP_PARAMETER_HIGHLIGHT_COLOR
from spyder.py3compat import to_text_string


HERE = osp.dirname(osp.abspath(__file__))
Expand Down Expand Up @@ -548,6 +549,71 @@ def test_delete(codeeditor):
assert editor.get_text_line(0) == ' f1(a, b):'


def test_copy_entire_line(codeeditor):
"""Test copying an entire line, if nothing is selected."""
editor = codeeditor
text = "import this\nmsg='Hello World!'\nprint(msg)"
editor.set_text(text)

# copy first line
cursor = editor.textCursor()
cursor.movePosition(QTextCursor.Start)
editor.setTextCursor(cursor)
editor.copy()

cb = QApplication.clipboard()
assert cb.text() == "import this\n"

# copy second line
cursor = editor.textCursor()
cursor.movePosition(QTextCursor.NextBlock)
editor.setTextCursor(cursor)
editor.copy()
cb = QApplication.clipboard()
assert cb.text() == "msg='Hello World!'\n"

# copy third line
cursor = editor.textCursor()
cursor.movePosition(QTextCursor.NextBlock)
editor.setTextCursor(cursor)
editor.copy()
cb = QApplication.clipboard()
# since it is the last line, the newline should be
# at the start with the current implementation
assert cb.text() == "\nprint(msg)"


def test_cut_entire_line(codeeditor):
"""Test cutting an entire line, if nothing is selected."""
editor = codeeditor
text = "import this\nmsg='Hello World!'\nprint(msg)"
editor.set_text(text)

# cut first line
cursor = editor.textCursor()
cursor.movePosition(QTextCursor.Start)
editor.setTextCursor(cursor)
editor.cut()

cb = QApplication.clipboard()
assert cb.text() == "import this\n"

# cut third line (tests last line case)
cb = QApplication.clipboard()
cursor = editor.textCursor()
cursor.movePosition(QTextCursor.NextBlock)
editor.setTextCursor(cursor)
editor.cut()
assert cb.text() == "\nprint(msg)"

# cut third line
editor.cut()
cb = QApplication.clipboard()
# since it is the last line in the document,
# there is no newline to cut, thus this has no newline anymore
assert cb.text() == "msg='Hello World!'"


def test_paste_files(codeeditor, copy_files_clipboard):
"""Test pasting files/folders into the editor."""
editor = codeeditor
Expand Down

0 comments on commit a672bb7

Please sign in to comment.