mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
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