Skip to content

Commit

Permalink
Updating the behaviour of cut in the TextArea to cut the whole line i…
Browse files Browse the repository at this point in the history
…f there is no selection
  • Loading branch information
darrenburns committed Dec 10, 2024
1 parent dfe9191 commit 6fa9f15
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 31 deletions.
21 changes: 11 additions & 10 deletions src/textual/widgets/_text_area.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,6 @@ class TextArea(ScrollView):
Binding(
"ctrl+f", "delete_word_right", "Delete right to start of word", show=False
),
Binding("ctrl+shift+x", "delete_line", "Delete line", show=False),
Binding("ctrl+x", "cut", "Cut", show=False),
Binding("ctrl+c", "copy", "Copy", show=False),
Binding("ctrl+v", "paste", "Paste", show=False),
Expand Down Expand Up @@ -2185,33 +2184,35 @@ def action_delete_right(self) -> None:

def action_delete_line(self) -> None:
"""Deletes the lines which intersect with the selection."""
self._delete_cursor_line()

def _delete_cursor_line(self) -> EditResult | None:
"""Deletes the line (including the line terminator) that the cursor is on."""
start, end = self.selection
start, end = sorted((start, end))
start_row, _start_column = start
end_row, end_column = end

# Generally editors will only delete line the end line of the
# selection if the cursor is not at column 0 of that line.
if start_row != end_row and end_column == 0 and end_row >= 0:
end_row -= 1

from_location = (start_row, 0)
to_location = (end_row + 1, 0)

deletion = self._delete_via_keyboard(from_location, to_location)
if deletion is not None:
self.move_cursor_relative(columns=end_column, record_width=False)
return deletion

def action_cut(self) -> None:
"""Cut text (remove and copy to clipboard)."""
if self.read_only:
return
start, end = self.selection
if start == end:
return
copy_text = self.get_text_range(start, end)
self.app.copy_to_clipboard(copy_text)
self._delete_via_keyboard(start, end)
edit_result = self._delete_cursor_line()
else:
edit_result = self._delete_via_keyboard(start, end)

if edit_result is not None:
self.app.copy_to_clipboard(edit_result.replaced_text)

def action_copy(self) -> None:
"""Copy selection to clipboard."""
Expand Down
34 changes: 13 additions & 21 deletions tests/text_area/test_edit_via_bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,26 +168,27 @@ async def test_delete_right_end_of_line():


@pytest.mark.parametrize(
"selection,expected_result",
"selection,expected_result,expected_clipboard,cursor_end_location",
[
(Selection.cursor((0, 0)), ""),
(Selection.cursor((0, 4)), ""),
(Selection.cursor((0, 10)), ""),
(Selection((0, 2), (0, 4)), ""),
(Selection((0, 4), (0, 2)), ""),
(Selection.cursor((0, 0)), "", "0123456789", (0, 0)),
(Selection.cursor((0, 4)), "", "0123456789", (0, 0)),
(Selection.cursor((0, 10)), "", "0123456789", (0, 0)),
(Selection((0, 2), (0, 4)), "01456789", "23", (0, 2)),
(Selection((0, 4), (0, 2)), "01456789", "23", (0, 2)),
],
)
async def test_delete_line(selection, expected_result):
async def test_cut(selection, expected_result, expected_clipboard, cursor_end_location):
app = TextAreaApp()
async with app.run_test() as pilot:
text_area = app.query_one(TextArea)
text_area.load_text("0123456789")
text_area.selection = selection

await pilot.press("ctrl+shift+x")
await pilot.press("ctrl+x")

assert text_area.selection == Selection.cursor((0, 0))
assert text_area.selection == Selection.cursor(cursor_end_location)
assert text_area.text == expected_result
assert app.clipboard == expected_clipboard


@pytest.mark.parametrize(
Expand All @@ -199,17 +200,8 @@ async def test_delete_line(selection, expected_result):
(Selection.cursor((3, 1)), "012\n345\n678\n"),
(Selection.cursor((4, 0)), "012\n345\n678\n9\n"),
# Selections
(Selection((1, 1), (1, 2)), "012\n678\n9\n"), # non-empty single line selection
(Selection((1, 2), (2, 1)), "012\n9\n"), # delete lines selection touches
(
Selection((1, 2), (3, 0)),
"012\n9\n",
), # cursor at column 0 of line 3, should not be deleted!
(
Selection((3, 0), (1, 2)),
"012\n9\n",
), # opposite direction
(Selection((0, 0), (4, 0)), ""), # delete all lines
(Selection((1, 1), (1, 2)), "012\n35\n678\n9\n"),
(Selection((1, 2), (2, 1)), "012\n3478\n9\n"),
],
)
async def test_delete_line_multiline_document(selection, expected_result):
Expand All @@ -219,7 +211,7 @@ async def test_delete_line_multiline_document(selection, expected_result):
text_area.load_text("012\n345\n678\n9\n")
text_area.selection = selection

await pilot.press("ctrl+shift+x")
await pilot.press("ctrl+x")

cursor_row, cursor_column = text_area.cursor_location
assert text_area.selection == Selection.cursor((cursor_row, cursor_column))
Expand Down

0 comments on commit 6fa9f15

Please sign in to comment.