mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Merge pull request #1763 from Textualize/deprecate-text-backend
remove text-backend
This commit is contained in:
@@ -1,159 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class TextEditorBackend:
|
||||
"""Represents a text editor (some text and a cursor)"""
|
||||
|
||||
content: str = ""
|
||||
cursor_index: int = 0
|
||||
|
||||
def set_content(self, text: str) -> None:
|
||||
"""Set the content of the editor
|
||||
|
||||
Args:
|
||||
text: The text to set as the content
|
||||
"""
|
||||
self.content = text
|
||||
|
||||
def delete_back(self) -> bool:
|
||||
"""Delete the character behind the cursor and moves cursor back. If the
|
||||
cursor is at the start of the content, does nothing other than immediately
|
||||
return False.
|
||||
|
||||
Returns:
|
||||
True if the text content was modified. False otherwise.
|
||||
"""
|
||||
if self.cursor_index == 0:
|
||||
return False
|
||||
|
||||
new_text = (
|
||||
self.content[: self.cursor_index - 1] + self.content[self.cursor_index :]
|
||||
)
|
||||
self.content = new_text
|
||||
self.cursor_index = max(0, self.cursor_index - 1)
|
||||
return True
|
||||
|
||||
def delete_forward(self) -> bool:
|
||||
"""Delete the character in front of the cursor without moving the cursor.
|
||||
|
||||
Returns:
|
||||
True if the text content was modified. False otherwise.
|
||||
"""
|
||||
if self.cursor_index == len(self.content):
|
||||
return False
|
||||
|
||||
new_text = (
|
||||
self.content[: self.cursor_index] + self.content[self.cursor_index + 1 :]
|
||||
)
|
||||
self.content = new_text
|
||||
return True
|
||||
|
||||
def cursor_left(self) -> bool:
|
||||
"""Move the cursor 1 character left in the text. Is a noop if cursor is at start.
|
||||
|
||||
Returns:
|
||||
True if the cursor moved. False otherwise.
|
||||
"""
|
||||
previous_index = self.cursor_index
|
||||
new_index = max(0, previous_index - 1)
|
||||
self.cursor_index = new_index
|
||||
return previous_index != new_index
|
||||
|
||||
def cursor_right(self) -> bool:
|
||||
"""Move the cursor 1 character right in the text. Is a noop if the cursor is at end.
|
||||
|
||||
Returns:
|
||||
True if the cursor moved. False otherwise.
|
||||
"""
|
||||
previous_index = self.cursor_index
|
||||
new_index = min(len(self.content), previous_index + 1)
|
||||
self.cursor_index = new_index
|
||||
return previous_index != new_index
|
||||
|
||||
def query_cursor_left(self) -> bool:
|
||||
"""Check if the cursor can move 1 codepoint left in the text.
|
||||
|
||||
Returns:
|
||||
True if the cursor can move left. False otherwise.
|
||||
"""
|
||||
previous_index = self.cursor_index
|
||||
new_index = max(0, previous_index - 1)
|
||||
return previous_index != new_index
|
||||
|
||||
def query_cursor_right(self) -> str | None:
|
||||
"""Check if the cursor can move right (we can't move right if we're at the end)
|
||||
and return the codepoint to the right of the cursor if it exists. If it doesn't
|
||||
exist (e.g. we're at the end), then return None
|
||||
|
||||
Returns:
|
||||
The codepoint to the right of the cursor if it exists, otherwise None.
|
||||
"""
|
||||
previous_index = self.cursor_index
|
||||
new_index = min(len(self.content), previous_index + 1)
|
||||
if new_index == len(self.content):
|
||||
return None
|
||||
elif previous_index != new_index:
|
||||
return self.content[new_index]
|
||||
return None
|
||||
|
||||
def cursor_text_start(self) -> bool:
|
||||
"""Move the cursor to the start of the text
|
||||
|
||||
Returns:
|
||||
True if the cursor moved. False otherwise.
|
||||
"""
|
||||
if self.cursor_index == 0:
|
||||
return False
|
||||
|
||||
self.cursor_index = 0
|
||||
return True
|
||||
|
||||
def cursor_text_end(self) -> bool:
|
||||
"""Move the cursor to the end of the text
|
||||
|
||||
Returns:
|
||||
True if the cursor moved. False otherwise.
|
||||
"""
|
||||
text_length = len(self.content)
|
||||
if self.cursor_index == text_length:
|
||||
return False
|
||||
|
||||
self.cursor_index = text_length
|
||||
return True
|
||||
|
||||
def insert(self, text: str) -> bool:
|
||||
"""Insert some text at the cursor position, and move the cursor
|
||||
to the end of the newly inserted text.
|
||||
|
||||
Args:
|
||||
text: The text to insert
|
||||
|
||||
Returns:
|
||||
Always returns True since text should be insertable regardless of cursor location
|
||||
"""
|
||||
new_text = (
|
||||
self.content[: self.cursor_index] + text + self.content[self.cursor_index :]
|
||||
)
|
||||
self.content = new_text
|
||||
self.cursor_index = min(len(self.content), self.cursor_index + len(text))
|
||||
return True
|
||||
|
||||
def get_range(self, start: int, end: int) -> str:
|
||||
"""Return the text between 2 indices. Useful for previews/views into
|
||||
a subset of the content e.g. scrollable single-line input fields
|
||||
|
||||
Args:
|
||||
start: The starting index to return text from (inclusive)
|
||||
end: The index to return text up to (exclusive)
|
||||
|
||||
Returns:
|
||||
The sliced string between start and end.
|
||||
"""
|
||||
return self.content[start:end]
|
||||
|
||||
@property
|
||||
def cursor_at_end(self):
|
||||
return self.cursor_index == len(self.content)
|
||||
@@ -1,165 +0,0 @@
|
||||
from textual._text_backend import TextEditorBackend
|
||||
|
||||
CONTENT = "Hello, world!"
|
||||
|
||||
|
||||
def test_set_content():
|
||||
editor = TextEditorBackend()
|
||||
editor.set_content(CONTENT)
|
||||
assert editor.content == CONTENT
|
||||
|
||||
|
||||
def test_delete_back_cursor_at_start_is_noop():
|
||||
editor = TextEditorBackend(CONTENT)
|
||||
assert not editor.delete_back()
|
||||
assert editor == TextEditorBackend(CONTENT, 0)
|
||||
|
||||
|
||||
def test_delete_back_cursor_at_end():
|
||||
editor = TextEditorBackend(CONTENT)
|
||||
assert editor.cursor_text_end()
|
||||
assert editor.delete_back()
|
||||
assert editor == TextEditorBackend("Hello, world", 12)
|
||||
|
||||
|
||||
def test_delete_back_cursor_in_middle():
|
||||
editor = TextEditorBackend(CONTENT, 5)
|
||||
assert editor.delete_back()
|
||||
assert editor == TextEditorBackend("Hell, world!", 4)
|
||||
|
||||
|
||||
def test_delete_forward_cursor_at_start():
|
||||
editor = TextEditorBackend(CONTENT)
|
||||
assert editor.delete_forward()
|
||||
assert editor.content == "ello, world!"
|
||||
|
||||
|
||||
def test_delete_forward_cursor_at_end_is_noop():
|
||||
editor = TextEditorBackend(CONTENT)
|
||||
assert editor.cursor_text_end()
|
||||
assert not editor.delete_forward()
|
||||
assert editor == TextEditorBackend(CONTENT, len(CONTENT))
|
||||
|
||||
|
||||
def test_delete_forward_cursor_in_middle():
|
||||
editor = TextEditorBackend(CONTENT, 5)
|
||||
editor.cursor_index = 5
|
||||
assert editor.delete_forward()
|
||||
assert editor == TextEditorBackend("Hello world!", 5)
|
||||
|
||||
|
||||
def test_cursor_left_cursor_at_start_is_noop():
|
||||
editor = TextEditorBackend(CONTENT)
|
||||
assert not editor.cursor_left()
|
||||
assert editor == TextEditorBackend(CONTENT)
|
||||
|
||||
|
||||
def test_cursor_left_cursor_in_middle():
|
||||
editor = TextEditorBackend(CONTENT, 6)
|
||||
assert editor.cursor_left()
|
||||
assert editor == TextEditorBackend(CONTENT, 5)
|
||||
|
||||
|
||||
def test_cursor_left_cursor_at_end():
|
||||
editor = TextEditorBackend(CONTENT, len(CONTENT))
|
||||
assert editor.cursor_left()
|
||||
assert editor == TextEditorBackend(CONTENT, len(CONTENT) - 1)
|
||||
|
||||
|
||||
def test_cursor_right_cursor_at_start():
|
||||
editor = TextEditorBackend(CONTENT)
|
||||
assert editor.cursor_right()
|
||||
assert editor == TextEditorBackend(CONTENT, 1)
|
||||
|
||||
|
||||
def test_cursor_right_cursor_in_middle():
|
||||
editor = TextEditorBackend(CONTENT, 5)
|
||||
assert editor.cursor_right()
|
||||
assert editor == TextEditorBackend(CONTENT, 6)
|
||||
|
||||
|
||||
def test_cursor_right_cursor_at_end_is_noop():
|
||||
editor = TextEditorBackend(CONTENT, len(CONTENT))
|
||||
editor.cursor_right()
|
||||
assert editor == TextEditorBackend(CONTENT, len(CONTENT))
|
||||
|
||||
|
||||
def test_query_cursor_left_cursor_at_start_returns_false():
|
||||
editor = TextEditorBackend(CONTENT)
|
||||
assert not editor.query_cursor_left()
|
||||
|
||||
|
||||
def test_query_cursor_left_cursor_at_end_returns_true():
|
||||
editor = TextEditorBackend(CONTENT, len(CONTENT))
|
||||
assert editor.query_cursor_left()
|
||||
|
||||
|
||||
def test_query_cursor_left_cursor_in_middle_returns_true():
|
||||
editor = TextEditorBackend(CONTENT, 6)
|
||||
assert editor.query_cursor_left()
|
||||
|
||||
|
||||
def test_query_cursor_right_cursor_at_start_returns_true():
|
||||
editor = TextEditorBackend(CONTENT)
|
||||
assert editor.query_cursor_right()
|
||||
|
||||
|
||||
def test_query_cursor_right_cursor_in_middle_returns_true():
|
||||
editor = TextEditorBackend(CONTENT, 6)
|
||||
assert editor.query_cursor_right()
|
||||
|
||||
|
||||
def test_query_cursor_right_cursor_at_end_returns_false():
|
||||
editor = TextEditorBackend(CONTENT, len(CONTENT))
|
||||
assert not editor.query_cursor_right()
|
||||
|
||||
|
||||
def test_cursor_text_start_cursor_already_at_start():
|
||||
editor = TextEditorBackend(CONTENT)
|
||||
assert not editor.cursor_text_start()
|
||||
assert editor.cursor_index == 0
|
||||
|
||||
|
||||
def test_cursor_text_start_cursor_in_middle():
|
||||
editor = TextEditorBackend(CONTENT, 6)
|
||||
assert editor.cursor_text_start()
|
||||
assert editor.cursor_index == 0
|
||||
|
||||
|
||||
def test_cursor_text_end_cursor_already_at_end():
|
||||
editor = TextEditorBackend(CONTENT, len(CONTENT))
|
||||
assert not editor.cursor_text_end()
|
||||
assert editor.cursor_index == len(CONTENT)
|
||||
|
||||
|
||||
def test_cursor_text_end_cursor_in_middle():
|
||||
editor = TextEditorBackend(CONTENT, len(CONTENT))
|
||||
assert not editor.cursor_text_end()
|
||||
assert editor.cursor_index == len(CONTENT)
|
||||
|
||||
|
||||
def test_insert_at_cursor_cursor_at_start():
|
||||
editor = TextEditorBackend(CONTENT)
|
||||
assert editor.insert("ABC")
|
||||
assert editor.content == "ABC" + CONTENT
|
||||
assert editor.cursor_index == len("ABC")
|
||||
|
||||
|
||||
def test_insert_at_cursor_cursor_in_middle():
|
||||
start_cursor_index = 6
|
||||
editor = TextEditorBackend(CONTENT, start_cursor_index)
|
||||
assert editor.insert("ABC")
|
||||
assert editor.content == "Hello,ABC world!"
|
||||
assert editor.cursor_index == start_cursor_index + len("ABC")
|
||||
|
||||
|
||||
def test_insert_at_cursor_cursor_at_end():
|
||||
editor = TextEditorBackend(CONTENT, len(CONTENT))
|
||||
assert editor.insert("ABC")
|
||||
assert editor.content == CONTENT + "ABC"
|
||||
assert editor.cursor_index == len(editor.content)
|
||||
|
||||
|
||||
def test_get_range():
|
||||
editor = TextEditorBackend(CONTENT)
|
||||
assert editor.get_range(0, 5) == "Hello"
|
||||
Reference in New Issue
Block a user