Skip to content

Widget

elva.widgets.ytextarea.widget

Widget definition.

Classes:

  • YTextArea

    Widget for displaying and manipulating text synchronized in realtime.

YTextArea(ytext, *args, **kwargs)

Bases: TextArea

Widget for displaying and manipulating text synchronized in realtime.

Parameters:

  • ytext (Text) –

    Y text data type holding the text.

  • args (tuple, default: () ) –

    positional arguments passed to TextArea.

  • kwargs (dict, default: {} ) –

    keyword arguments passed to TextArea.

Methods:

Attributes:

Source code in src/elva/widgets/ytextarea/widget.py
def __init__(self, ytext: Text, *args: tuple, **kwargs: dict):
    """
    Arguments:
        ytext: Y text data type holding the text.
        args: positional arguments passed to [`TextArea`][textual.widgets.TextArea].
        kwargs: keyword arguments passed to [`TextArea`][textual.widgets.TextArea].
    """
    super().__init__(str(ytext), *args, **kwargs)
    self.ytext = ytext
    self.origin = ytext.doc.client_id

    # record changes in the YText;
    # overwrites TextArea.history
    self.history = UndoManager(
        scopes=[ytext],
        capture_timeout_millis=300,
    )

    # perform undo and redo solely on our contributions
    self.history.include_origin(self.origin)

DEFAULT_CSS = '\n YTextArea {\n border: none;\n padding: 0;\n background: transparent;\n\n &:focus {\n border: none;\n }\n }\n ' class-attribute instance-attribute

Default CSS.

ytext = ytext instance-attribute

The Y Text data type holding the text.

origin = ytext.doc.client_id instance-attribute

The own origin of edits.

history = UndoManager(scopes=[ytext], capture_timeout_millis=300) instance-attribute

The history manager for undo and redo operations.

code_editor(ytext, *args, **kwargs) classmethod

Construct a text area with coding specific settings.

Parameters:

  • ytext (Text) –

    the Y Text data type holding the text.

  • args (tuple, default: () ) –

    positional arguments passed to TextArea.

  • kwargs (dict, default: {} ) –

    keyword arguments passed to TextArea.

Returns:

Source code in src/elva/widgets/ytextarea/widget.py
@classmethod
def code_editor(cls, ytext: Text, *args: tuple, **kwargs: dict) -> Self:
    """
    Construct a text area with coding specific settings.

    Arguments:
        ytext: the Y Text data type holding the text.
        args: positional arguments passed to [`TextArea`][textual.widgets.TextArea].
        kwargs: keyword arguments passed to [`TextArea`][textual.widgets.TextArea].

    Returns:
        an instance of [`YTextArea`][elva.widgets.ytextarea.YTextArea].
    """
    return cls(ytext, *args, **kwargs)

get_index_from_binary_index(index)

Convert the index in UTF-8 encoding to character index.

Parameters:

  • index (int) –

    index in UTF-8 encoded text.

Returns:

  • int

    index in the UTF-8 decoded form of btext.

Source code in src/elva/widgets/ytextarea/widget.py
def get_index_from_binary_index(self, index: int) -> int:
    """
    Convert the index in UTF-8 encoding to character index.

    Arguments:
        index: index in UTF-8 encoded text.

    Returns:
        index in the UTF-8 decoded form of `btext`.
    """
    return len(self.document.text.encode()[:index].decode())

get_binary_index_from_index(index)

Convert the character index to index in UTF-8 encoding.

Parameters:

  • index (int) –

    index in UTF-8 decoded text.

Returns:

  • int

    index in the UTF-8 encoded form of text.

Source code in src/elva/widgets/ytextarea/widget.py
def get_binary_index_from_index(self, index: int) -> int:
    """
    Convert the character index to index in UTF-8 encoding.

    Arguments:
        index: index in UTF-8 decoded text.

    Returns:
        index in the UTF-8 encoded form of `text`.
    """
    return len(self.document.text[:index].encode())

get_location_from_binary_index(index)

Convert binary index to document location.

Parameters:

  • index (int) –

    index in the UTF-8 encoded text.

Returns:

  • tuple

    a location with containing row and column coordinates.

Source code in src/elva/widgets/ytextarea/widget.py
def get_location_from_binary_index(self, index: int) -> tuple:
    """
    Convert binary index to document location.

    Arguments:
        index: index in the UTF-8 encoded text.

    Returns:
        a location with containing row and column coordinates.
    """
    index = self.get_index_from_binary_index(index)
    return self.document.get_location_from_index(index)

get_binary_index_from_location(location)

Convert location to binary index.

Parameters:

  • location (tuple) –

    row and column coordinates.

Returns:

  • int

    the index in the UTF-8 encoded text.

Source code in src/elva/widgets/ytextarea/widget.py
def get_binary_index_from_location(self, location: tuple) -> int:
    """
    Convert location to binary index.

    Arguments:
        location: row and column coordinates.

    Returns:
        the index in the UTF-8 encoded text.
    """
    index = self.document.get_index_from_location(location)
    return self.get_binary_index_from_index(index)

on_textevent(event)

Hook called on changes in the Y text data type:

It parses the event and applies the edit to the document.

Parameters:

  • event (TextEvent) –

    an object holding edit information.

Source code in src/elva/widgets/ytextarea/widget.py
def on_textevent(self, event: TextEvent):
    """
    Hook called on changes in the Y text data type:

    It parses the event and applies the edit to the document.

    Arguments:
        event: an object holding edit information.
    """
    istart = 0
    length = 0
    insert = ""

    for delta in event.delta:
        for action, var in delta.items():
            if action == "retain":
                istart = var
            elif action == "delete":
                length = var
            elif action == "insert":
                insert = var

    iend = istart + length

    start = self.get_location_from_binary_index(istart)
    end = self.get_location_from_binary_index(iend)

    self._apply_update(insert, start, end)

on_mount()

Hook called on mounting.

It adds a subscription to changes in the Y text data type.

Source code in src/elva/widgets/ytextarea/widget.py
def on_mount(self):
    """
    Hook called on mounting.

    It adds a subscription to changes in the Y text data type.
    """
    self.subscription_textevent = self.ytext.observe(self.on_textevent)

on_unmount()

Hook called on unmounting.

It cancels the subscription to changes in the Y text data type.

Source code in src/elva/widgets/ytextarea/widget.py
def on_unmount(self):
    """
    Hook called on unmounting.

    It cancels the subscription to changes in the Y text data type.
    """
    self.ytext.unobserve(self.subscription_textevent)
    del self.subscription_textevent

load_text(text, language=None)

Load a text into the document.

Parameters:

  • text (str) –

    the text to display.

  • language (str | None, default: None ) –

    the tree-sitter syntax highlighting language to use.

Source code in src/elva/widgets/ytextarea/widget.py
def load_text(self, text: str, language: str | None = None):
    """
    Load a text into the document.

    Arguments:
        text: the text to display.
        language: the tree-sitter syntax highlighting language to use.
    """
    self.replace(text, self.document.start, self.document.end)

    if not self.is_mounted:
        self.document.replace_range(self.document.start, self.document.end, text)

    if language:
        self.language = language

    self.post_message(self.Changed(self).set_sender(self))

replace(insert, start, end)

Replace part of the text in the Y text data type.

Parameters:

  • insert (str) –

    the characters to insert.

  • start (tuple) –

    the start location of the deletion range.

  • end (tuple) –

    the end location of the deletion range.

Source code in src/elva/widgets/ytextarea/widget.py
def replace(
    self,
    insert: str,
    start: tuple,
    end: tuple,
):
    """
    Replace part of the text in the Y text data type.

    Arguments:
        insert: the characters to insert.
        start: the start location of the deletion range.
        end: the end location of the deletion range.
    """
    start, end = sorted((start, end))

    doc = self.ytext.doc

    istart = self.get_binary_index_from_location(start)
    iend = self.get_binary_index_from_location(end)

    # perform an atomic edit
    with doc.transaction(origin=self.origin):
        if not istart == iend:
            del self.ytext[istart:iend]

        if insert:
            self.ytext.insert(istart, insert)

delete(start, end)

Delete a range of text.

Parameters:

  • start (tuple) –

    the start location of the deletion range.

  • end (tuple) –

    the end location of the deletion range.

Source code in src/elva/widgets/ytextarea/widget.py
def delete(self, start: tuple, end: tuple):
    """
    Delete a range of text.

    Arguments:
        start: the start location of the deletion range.
        end: the end location of the deletion range.
    """
    start, end = sorted((start, end))
    self.replace("", start, end)

insert(text, location=None)

Insert characters at a given location.

Parameters:

  • text (str) –

    the characters to insert.

  • location (tuple, default: None ) –

    the start location of the insertion.

Source code in src/elva/widgets/ytextarea/widget.py
def insert(self, text: str, location: tuple = None):
    """
    Insert characters at a given location.

    Arguments:
        text: the characters to insert.
        location: the start location of the insertion.
    """
    if location is None:
        location = self.cursor_location

    self.replace(text, location, location)

clear()

Remove all content from the document.

Source code in src/elva/widgets/ytextarea/widget.py
def clear(self):
    """
    Remove all content from the document.
    """
    self.replace("", self.document.start, self.document.end)

_replace_via_keyboard(insert, start, end)

Guard method respecting the read_only attribute before calling replace to replace a range of text.

Parameters:

  • insert (str) –

    the characters to insert.

  • start (tuple) –

    the start location of the deletion range.

  • end (tuple) –

    the end location of the deletion range.

Source code in src/elva/widgets/ytextarea/widget.py
def _replace_via_keyboard(self, insert: str, start: tuple, end: tuple):
    """
    Guard method respecting the [`read_only`][textual.widgets.TextArea.read_only]
    attribute before calling [`replace`][elva.widgets.ytextarea.YTextArea]
    to replace a range of text.

    Arguments:
        insert: the characters to insert.
        start: the start location of the deletion range.
        end: the end location of the deletion range.
    """
    if self.read_only:
        return

    self.replace(insert, start, end)

_delete_via_keyboard(start, end)

Guard method respecting the read_only attribute before calling replace to delete a range of text.

Parameters:

  • start (tuple) –

    the start location of the deletion range.

  • end (tuple) –

    the end location of the deletion range.

Source code in src/elva/widgets/ytextarea/widget.py
def _delete_via_keyboard(self, start: tuple, end: tuple):
    """
    Guard method respecting the [`read_only`][textual.widgets.TextArea.read_only]
    attribute before calling [`replace`][elva.widgets.ytextarea.YTextArea]
    to delete a range of text.

    Arguments:
        start: the start location of the deletion range.
        end: the end location of the deletion range.
    """
    self._replace_via_keyboard("", start, end)

_apply_update(text, start, end)

Apply a Y text data type update to the document.

Parameters:

  • text (str) –

    the characters to insert.

  • start (tuple) –

    the start location of the deletion range.

  • end (tuple) –

    the end location of the deletion range.

Source code in src/elva/widgets/ytextarea/widget.py
def _apply_update(self, text: str, start: tuple, end: tuple):
    """
    Apply a Y text data type update to the document.

    Arguments:
        text: the characters to insert.
        start: the start location of the deletion range.
        end: the end location of the deletion range.
    """
    old_gutter_width = self.gutter_width

    # replaces edit.do(self)
    selection, top, bottom, insert_end = self._edit(text, start, end)

    new_gutter_width = self.gutter_width

    if old_gutter_width != new_gutter_width:
        self.wrapped_document.wrap(
            self.wrap_width,
            self.indent_width,
        )
    else:
        self.wrapped_document.wrap_range(
            top,
            bottom,
            insert_end,
        )

    self._refresh_size()

    # replaces edit.after(self)
    self.selection = selection
    self.record_cursor_width()

    self._build_highlight_map()
    self.post_message(self.Changed(self))

_edit(text, top, bottom)

Perform the edit operation.

Parameters:

  • text (str) –

    the characters to insert.

  • top (tuple) –

    the minimum of start and end location of the deletion range.

  • bottom (tuple) –

    the maximum of start and end location of the deletion range.

Returns:

Source code in src/elva/widgets/ytextarea/widget.py
def _edit(
    self, text: str, top: tuple, bottom: tuple
) -> tuple[Selection, tuple, tuple, tuple]:
    """
    Perform the edit operation.

    Args:
        text: the characters to insert.
        top: the minimum of start and end location of the deletion range.
        bottom: the maximum of start and end location of the deletion range.

    Returns:
        the updated selection, top and bottom locations as well as the end location of the insertion range.
    """

    edit_result = self.document.replace_range(top, bottom, text)

    start, end = self.selection
    insert_end = edit_result.end_location

    delete = Selection(top, bottom)
    insert = Selection(top, insert_end)

    if (start in delete) and (end in delete):
        # the current selection has been deleted, i.e. is within the deletion range;
        # reset cursor to end of edit, i.e. insert range
        start, end = insert.end, insert.end
    else:
        # reverse target locations if the current selection is reversed
        if start > end:
            target_start, target_end = insert.start, insert.end
        else:
            target_start, target_end = insert.end, insert.start

        ## start
        # before edit - no-op
        # within edit - shift to end of edit
        #  after edit - shift by edit length

        ## end
        # before edit - no-op
        # within edit - shift to start of edit
        #  after edit - shift by edit length

        start = update_location(start, delete, insert, target_start)
        end = update_location(end, delete, insert, target_end)

    selection = Selection(start, end)
    return selection, top, bottom, insert_end

undo()

Undo an edit done by this widget.

Source code in src/elva/widgets/ytextarea/widget.py
def undo(self):
    """
    Undo an edit done by this widget.
    """
    self.history.undo()

redo()

Redo an edit done by this widget.

Source code in src/elva/widgets/ytextarea/widget.py
def redo(self):
    """
    Redo an edit done by this widget.
    """
    self.history.redo()

_watch_language(new)

Hook called on change in the language attribute.

Parameters:

  • new (str | None) –

    the new language.

Source code in src/elva/widgets/ytextarea/widget.py
def _watch_language(self, new: str | None):
    """
    Hook called on change in the [`language`][textual.widgets.TextArea.language] attribute.

    Arguments:
        new: the new language.
    """
    self._highlight_query = None

    if not new:
        return

    if not TREE_SITTER:
        self.log.warning("tree-sitter not supported in this environment")
        return

    registered = self._languages.get(new)

    if registered:
        query = registered.highlight_query
        lang = registered.language or get_language(new)
    else:
        query = self._get_builtin_highlight_query(new)
        lang = get_language(new)

    if lang is not None:
        self._highlight_query = self.document.prepare_query(query)
    else:
        self.log.warning(f"tree-sitter language '{new}' not found")

    self._build_highlight_map()

_watch_has_focus(new)

Hook called on change of the has_focus attribute.

Parameters:

  • new (bool) –

    the new value.

Source code in src/elva/widgets/ytextarea/widget.py
def _watch_has_focus(self, new: bool):
    """
    Hook called on change of the [`has_focus`][textual.widget.Widget.has_focus] attribute.

    Arguments:
        new: the new value.
    """
    self._cursor_visible = new

    if new:
        self._restart_blink()
        self.app.cursor_position = self.cursor_screen_offset
    else:
        self._pause_blink(visible=False)

_on_mouse_down(event) async

Hook on a pressed mouse button.

Parameters:

  • event (MouseDown) –

    an object containing event information.

Source code in src/elva/widgets/ytextarea/widget.py
async def _on_mouse_down(self, event: MouseDown):
    """
    Hook on a pressed mouse button.

    Arguments:
        event: an object containing event information.
    """
    event.stop()
    event.prevent_default()
    target = self.get_target_document_location(event)
    self.selection = Selection.cursor(target)
    self._selecting = True

    self.capture_mouse()
    self._pause_blink(visible=True)

move_cursor(location, select=False, center=False, record_width=True)

Move the cursor to a given location and adapt the scroll position.

Parameters:

  • location (tuple) –

    the location to move the cursor to

  • select (bool, default: False ) –

    flag whether to expand the current selection or just move the cursor.

  • center (bool, default: False ) –

    flag whether to scroll the view.

  • record_width (bool, default: True ) –

    flag whether to record the cursor width.

Source code in src/elva/widgets/ytextarea/widget.py
def move_cursor(
    self,
    location: tuple,
    select: bool = False,
    center: bool = False,
    record_width: bool = True,
):
    """
    Move the cursor to a given location and adapt the scroll position.

    Arguments:
        location: the location to move the cursor to
        select: flag whether to expand the current selection or just move the cursor.
        center: flag whether to scroll the view.
        record_width: flag whether to record the cursor width.
    """
    if select:
        start, _ = self.selection
        self.selection = Selection(start, location)
    else:
        self.selection = Selection.cursor(location)

    if record_width:
        self.record_cursor_width()

    if center:
        self.scroll_cursor_visible(center)