Skip to content

Commit

Permalink
Fix crash with backwards selection where content is replaced with few…
Browse files Browse the repository at this point in the history
…er lines of text
  • Loading branch information
darrenburns committed Feb 6, 2024
1 parent ca2c11b commit cdd08ed
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 8 deletions.
21 changes: 13 additions & 8 deletions src/textual/widgets/_text_area.py
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,6 @@ def render_line(self, widget_y: int) -> Strip:
# Get the line from the Document.
line_string = document.get_line(line_index)
line = Text(line_string, end="")

line_character_count = len(line)
line.tab_size = self.indent_width
line.set_length(line_character_count + 1) # space at end for cursor
Expand Down Expand Up @@ -1195,8 +1194,8 @@ def edit(self, edit: Edit) -> EditResult:
self.wrapped_document.wrap(self.wrap_width, self.indent_width)
else:
self.wrapped_document.wrap_range(
edit.from_location,
edit.to_location,
edit.top,
edit.bottom,
result.end_location,
)

Expand Down Expand Up @@ -1811,8 +1810,7 @@ def delete(
Returns:
An `EditResult` containing information about the edit.
"""
top, bottom = sorted((start, end))
return self.edit(Edit("", top, bottom, maintain_selection_offset))
return self.edit(Edit("", start, end, maintain_selection_offset))

def replace(
self,
Expand Down Expand Up @@ -1986,14 +1984,13 @@ def do(self, text_area: TextArea) -> EditResult:
# position in the document even if an insert happens before
# their cursor position.

edit_top, edit_bottom = sorted((edit_from, edit_to))
edit_bottom_row, edit_bottom_column = edit_bottom
edit_bottom_row, edit_bottom_column = self.bottom

selection_start, selection_end = text_area.selection
selection_start_row, selection_start_column = selection_start
selection_end_row, selection_end_column = selection_end

replace_result = text_area.document.replace_range(edit_from, edit_to, text)
replace_result = text_area.document.replace_range(self.top, self.bottom, text)

new_edit_to_row, new_edit_to_column = replace_result.end_location

Expand Down Expand Up @@ -2048,6 +2045,14 @@ def after(self, text_area: TextArea) -> None:
text_area.selection = self._updated_selection
text_area.record_cursor_width()

@property
def top(self) -> Location:
return min([self.from_location, self.to_location])

@property
def bottom(self) -> Location:
return max([self.from_location, self.to_location])


@runtime_checkable
class Undoable(Protocol):
Expand Down
18 changes: 18 additions & 0 deletions tests/text_area/test_edit_via_bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
Note that more extensive testing for editing is done at the Document level.
"""

import pytest

from textual.app import App, ComposeResult
Expand Down Expand Up @@ -416,3 +417,20 @@ async def test_delete_word_right_at_end_of_line():

assert text_area.text == "0123456789"
assert text_area.selection == Selection.cursor((0, 5))


async def test_replace_lines_with_fewer_lines_backwards_selection():
app = TextAreaApp()
async with app.run_test() as pilot:
text_area = app.query_one(TextArea)
text_area.text = SIMPLE_TEXT
text_area.selection = Selection(start=(3, 0), end=(1, 0))

await pilot.press("a")

expected_text = """\
ABCDE
aPQRST
UVWXY
Z"""
assert text_area.text == expected_text

0 comments on commit cdd08ed

Please sign in to comment.