Skip to content

TextArea

elva.widgets.textarea

Textual Widgets for realtime text-editing

Classes:

  • YDocument

    The inner document holding the realtime synchronized content.

  • YTextArea

    Widget for displaying and manipulating text synchronized in realtime.

Functions:

Attributes:

NEWLINE_CHARS = '\n\r' module-attribute

String of newline characters.

YDocument(ytext, language)

Bases: DocumentBase

The inner document holding the realtime synchronized content.

It supports indexing and implements the asynchronous iterator protocol.

This class is intended for use in YTextArea.

Examples:

Indexing:

text = r"Hello,\nWorld!"
ytext = Text(text)
doc = YDocument(ytext, "python")
assert doc[0] == "Hello,"

Asynchronous iterator protocol:

async for edit in doc:
    do_something(edit)

Parameters:

  • ytext (Text) –

    Y text data type holding the document content.

  • language (str) –

    the language the document content is written in.

Methods:

Attributes:

  • ytext (Text) –

    Y text data type holding the document content.

  • edits (Queue) –

    Queue of applied edits.

  • language (Language) –

    Instance of the tree-sitter language used for queries.

  • parser (Parser) –

    Instance of the tree-sitter parser.

  • tree (Tree) –

    Instance of the tree-sitter tree the parser updates incrementally.

  • syntax_enabled (bool) –

    Flag whether to apply tree-sitter queries.

  • text (str) –

    Text representation of the Y text data type content.

  • btext (bytes) –

    UTF-8 encoded text representation of the Y text data type content.

  • newline (str) –

    The newline character used in this document.

  • lines (list[str]) –

    The document's content formatted as a list of lines.

  • line_count (int) –

    The number of lines.

  • start (tuple[int, int]) –

    Start location of the document's text.

  • end (tuple[int, int]) –

    End location of the document's text.

Source code in src/elva/widgets/textarea.py
def __init__(self, ytext: Text, language: str):
    """
    Arguments:
        ytext: Y text data type holding the document content.
        language: the language the document content is written in.

    """
    self.ytext = ytext
    self.ytext.observe(self.callback)
    self._newline = _detect_newline_style(str(ytext))
    self.edits = Queue()

    try:
        self.language = get_language(language)
        self.parser = get_parser(language)
        self.tree = self.parser.parse(self.get_btext_slice)
        self.syntax_enabled = True
    except LookupError:
        self.syntax_enabled = False

ytext = ytext instance-attribute

Y text data type holding the document content.

edits = Queue() instance-attribute

Queue of applied edits.

language = get_language(language) instance-attribute

Instance of the tree-sitter language used for queries.

parser = get_parser(language) instance-attribute

Instance of the tree-sitter parser.

tree = self.parser.parse(self.get_btext_slice) instance-attribute

Instance of the tree-sitter tree the parser updates incrementally.

syntax_enabled = True instance-attribute

Flag whether to apply tree-sitter queries.

text property

Text representation of the Y text data type content.

btext property

UTF-8 encoded text representation of the Y text data type content.

newline property

The newline character used in this document.

lines property

The document's content formatted as a list of lines.

line_count property

The number of lines.

start property

Start location of the document's text.

end property

End location of the document's text.

get_line(row)

Get the line's content at a specified row.

Parameters:

  • row (int) –

    the row index.

Returns:

  • str

    the document content in the specified row.

Source code in src/elva/widgets/textarea.py
def get_line(self, row: int) -> str:
    """
    Get the line's content at a specified row.

    Arguments:
        row: the row index.

    Returns:
        the document content in the specified row.
    """
    return self.lines[row]

__getitem__(row)

Get the line's content at a specified row.

Parameters:

  • row (int) –

    the row index.

Returns:

  • str

    the document content in the specified row.

Source code in src/elva/widgets/textarea.py
def __getitem__(self, row: int) -> str:
    """
    Get the line's content at a specified row.

    Arguments:
        row: the row index.

    Returns:
        the document content in the specified row.
    """
    return self.lines[row]

get_binary_index_from_index(index)

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

Parameters:

  • index (int) –

    index in the document's text.

Returns:

  • int

    index in the document's UTF-8 encoded text.

Source code in src/elva/widgets/textarea.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 the document's text.

    Returns:
        index in the document's UTF-8 encoded text.
    """
    return get_binary_index_from_index(self.btext, index)

get_index_from_location(location)

Convert 2D row and column coordinates to index position.

Parameters:

  • location (tuple[int, int]) –

    a tuple of row and column coordinates.

Returns:

  • int

    the index in the document's text.

Source code in src/elva/widgets/textarea.py
def get_index_from_location(self, location: tuple[int, int]) -> int:
    """
    Convert 2D row and column coordinates to index position.

    Arguments:
        location: a tuple of row and column coordinates.

    Returns:
        the index in the document's text.
    """
    return get_index_from_location(self.text, location)

get_binary_index_from_location(location)

Convert location to binary index.

Parameters:

  • location (tuple[int, int]) –

    a tuple of row and column coordinates.

Returns:

  • int

    the index in the document's UTF-8 encoded text.

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

    Arguments:
        location: a tuple of row and column coordinates.

    Returns:
        the index in the document's UTF-8 encoded text.
    """
    return get_binary_index_from_location(self.btext, location)

get_index_from_binary_index(index)

Convert binary index to index.

Parameters:

  • index (int) –

    index in the document's UTF-8 encoded text.

Returns:

  • int

    the index in the document's text.

Source code in src/elva/widgets/textarea.py
def get_index_from_binary_index(self, index: int) -> int:
    """
    Convert binary index to index.

    Arguments:
        index: index in the document's UTF-8 encoded text.

    Returns:
        the index in the document's text.
    """
    return get_index_from_binary_index(self.btext, index)

get_location_from_index(index)

Convert index to location.

Parameters:

  • index (int) –

    index in the document's text.

Returns:

  • tuple[int, int]

    the location in the document's text.

Source code in src/elva/widgets/textarea.py
def get_location_from_index(self, index: int) -> tuple[int, int]:
    """
    Convert index to location.

    Arguments:
        index: index in the document's text.

    Returns:
        the location in the document's text.
    """
    return get_location_from_index(self.text, index)

get_location_from_binary_index(index)

Convert binary index to location.

Parameters:

  • index (int) –

    index in the document's UTF-8 encoded text.

Returns:

  • tuple[int, int]

    the location in the document's text.

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

    Arguments:
        index: index in the document's UTF-8 encoded text.

    Returns:
        the location in the document's text.
    """
    return get_location_from_binary_index(self.btext, index)

get_text_range(start, end)

Get the text within a certain range.

Parameters:

  • start (tuple[int, int]) –

    location where the text range starts.

  • end (tuple[int, int]) –

    location where the text range ends.

Returns:

  • str

    the slice of text within start and end location.

Source code in src/elva/widgets/textarea.py
def get_text_range(self, start: tuple[int, int], end: tuple[int, int]) -> str:
    """
    Get the text within a certain range.

    Arguments:
        start: location where the text range starts.
        end: location where the text range ends.

    Returns:
        the slice of text within `start` and `end` location.
    """
    return get_text_range(self.text, start, end)

get_size(indent_width)

Get the size of the document's text.

Parameters:

  • indent_width (int) –

    number of spaces to replace with tabs.

Returns:

  • Size

    object holding the number of columns and rows.

Source code in src/elva/widgets/textarea.py
def get_size(self, indent_width: int) -> Size:
    """
    Get the size of the document's text.

    Arguments:
        indent_width: number of spaces to replace with tabs.

    Returns:
        object holding the number of columns and rows.
    """
    lines = self.lines
    rows = len(lines)
    cell_lengths = [cell_len(line.expandtabs(indent_width)) for line in lines]
    cols = max(cell_lengths, default=0)
    return Size(cols, rows)

replace_range(start, end, text)

Apply an edit.

First, the range from start to end gets deleted, then the given text gets inserted at start.

This covers all kinds of text manipulation: - inserting: start == end, text is not empty - deleting: start != end, text is empty - replacing: start != end, text is not empty

Parameters:

  • start (tuple[int, int]) –

    location where the edit begins

  • end (tuple[int, int]) –

    location where the edit ends

  • text (str) –

    the text to insert at start

Source code in src/elva/widgets/textarea.py
def replace_range(self, start: tuple[int, int], end: tuple[int, int], text: str):
    """
    Apply an edit.

    First, the range from `start` to `end` gets deleted, then the given `text` gets inserted at `start`.

    This covers all kinds of text manipulation:
    - inserting: `start == end`, `text` is not empty
    - deleting: `start != end`, `text` is empty
    - replacing: `start != end`, `text` is not empty

    Arguments:
        start: location where the edit begins
        end: location where the edit ends
        text: the text to insert at `start`
    """
    start, end = sorted((start, end))
    bstart, bend = [
        self.get_binary_index_from_location(location) for location in (start, end)
    ]

    doc = self.ytext.doc

    # make transaction atomic and include an origin for the provider
    with doc.transaction(origin="ydocument"):
        if not start == end:
            del self.ytext[bstart:bend]
        if text:
            self.ytext.insert(bstart, text)

update_tree(istart, iend_old, iend, start, end_old, end)

Update the the syntax tree if syntax is enabled.

This method is called by the class internally.

Parameters:

  • istart (int) –

    binary start index

  • iend_old (int) –

    binary end index before the edit

  • iend (int) –

    binary end index after the edit

  • start (tuple[int, int]) –

    binary start location

  • end_old (tuple[int, int]) –

    binary end location before the edit.

  • end (tuple[int, int]) –

    binary end location after the edit.

Source code in src/elva/widgets/textarea.py
def update_tree(
    self,
    istart: int,
    iend_old: int,
    iend: int,
    start: tuple[int, int],
    end_old: tuple[int, int],
    end: tuple[int, int],
):
    """
    Update the the syntax tree if syntax is enabled.

    This method is called by the class internally.

    Arguments:
        istart: binary start index
        iend_old: binary end index before the edit
        iend: binary end index after the edit
        start: binary start location
        end_old: binary end location before the edit.
        end: binary end location after the edit.
    """
    if self.syntax_enabled:
        self.tree.edit(istart, iend_old, iend, start, end_old, end)
        self.tree = self.parser.parse(self.get_btext_slice, old_tree=self.tree)

get_btext_slice(byte_offset, position)

Get a slice of the document's UTF-8 encoded text.

This method is called by the class internally.

Parameters:

  • byte_offset (int) –

    binary start index of the slice.

  • position (Point) –

    binary start location of the slice.

Returns:

  • bytes

    line chunk starting at byte_offset or an empty bytes literal if at the end of document's UTF-8 encoded text.

Source code in src/elva/widgets/textarea.py
def get_btext_slice(self, byte_offset: int, position: Point) -> bytes:
    """
    Get a slice of the document's UTF-8 encoded text.

    This method is called by the class internally.

    Arguments:
        byte_offset: binary start index of the slice.
        position: binary start location of the slice.

    Returns:
        line chunk starting at `byte_offset` or an empty bytes literal if at the end of document's UTF-8 encoded text.
    """
    lines = self.btext[byte_offset:].splitlines(keepends=True)
    if lines:
        return lines[0]
    else:
        return b""

query_syntax_tree(query, start=None, end=None)

Get captures for a query on the syntax tree.

Parameters:

  • query (Query) –

    instance of a tree-sitter query

  • start (Point, default: None ) –

    start point of the query

  • end (Point, default: None ) –

    end point of the query

Returns:

  • dict[str, list[Node]]

    a dict where the keys are the names of the captures and the values are lists of the captured nodes.

Source code in src/elva/widgets/textarea.py
def query_syntax_tree(
    self, query: Query, start: Point = None, end: Point = None
) -> dict[str, list[Node]]:
    """
    Get captures for a query on the syntax tree.

    Arguments:
        query: instance of a tree-sitter query
        start: start point of the query
        end: end point of the query

    Returns:
        a dict where the keys are the names of the captures and the values are lists of the captured nodes.
    """
    kwargs = {}
    if start is not None:
        kwargs["start_point"] = start
    if end is not None:
        kwargs["end_point"] = end
    captures = query.captures(self.tree.root_node, **kwargs)
    return captures

parse(event)

Parse the contents of a document's Y text data type event.

This method is called by the class internally.

Parameters:

  • event (TextEvent) –

    event emitted by a change in the document's Y text data type.

Source code in src/elva/widgets/textarea.py
def parse(self, event: TextEvent):
    """
    Parse the contents of a document's Y text data type event.

    This method is called by the class internally.

    Arguments:
        event: event emitted by a change in the document's Y text data type.
    """
    deltas = event.delta

    range_offset = 0
    for delta in deltas:
        for action, var in delta.items():
            match action:
                case "retain":
                    range_offset = var
                    self.on_retain(range_offset)
                case "insert":
                    insert_value = var
                    self.on_insert(range_offset, insert_value)
                case "delete":
                    range_length = var
                    self.on_delete(range_offset, range_length)

callback(event)

Hook called on a document's Y text data type event.

Parameters:

  • event (TextEvent) –

    event emitted by a change in the document's Y text data type.

Source code in src/elva/widgets/textarea.py
def callback(self, event: TextEvent):
    """
    Hook called on a document's Y text data type event.

    Arguments:
        event: event emitted by a change in the document's Y text data type.
    """
    self.parse(event)

on_retain(range_offset)

Hook called on a retain actio in a document's Y text data type event.

No further actions are taken.

Parameters:

  • range_offset (int) –

    start index of the retain action.

Source code in src/elva/widgets/textarea.py
def on_retain(self, range_offset: int):
    """
    Hook called on a retain actio in a document's Y text data type event.

    No further actions are taken.

    Arguments:
        range_offset: start index of the retain action.
    """
    pass

on_insert(range_offset, insert_value)

Hook called on an insert action in a document's Y text data type event.

It puts edit metrics as tuples into the internal edits queue and updates the syntax tree.

Parameters:

  • range_offset (int) –

    start index of insertion.

  • insert_value (str) –

    string to insert.

Source code in src/elva/widgets/textarea.py
def on_insert(self, range_offset: int, insert_value: str):
    """
    Hook called on an insert action in a document's Y text data type event.

    It puts edit metrics as tuples into the internal edits queue and updates the syntax tree.

    Arguments:
        range_offset: start index of insertion.
        insert_value: string to insert.
    """
    bstart = range_offset
    btext = insert_value.encode()

    self.edits.put_nowait((bstart, bstart, btext))

    # syntax highlighting
    istart = bstart
    iend_old = bstart
    iend = bstart + len(btext)
    start = get_binary_location_from_binary_index(self.btext, istart)
    end_old = start
    end = get_binary_location_from_binary_index(self.btext, iend)
    self.update_tree(istart, iend_old, iend, start, end_old, end)

on_delete(range_offset, range_length)

Hook called on an insert action in a document's Y text data type event.

It puts edit metrics as tuples into the internal edits queue and updates the syntax tree.

Parameters:

  • range_offset (int) –

    start index of deletion.

  • range_length (int) –

    length of the text slice to delete.

Source code in src/elva/widgets/textarea.py
def on_delete(self, range_offset: int, range_length: int):
    """
    Hook called on an insert action in a document's Y text data type event.

    It puts edit metrics as tuples into the internal edits queue and updates the syntax tree.

    Arguments:
        range_offset: start index of deletion.
        range_length: length of the text slice to delete.
    """
    bstart = range_offset
    bend = range_offset + range_length

    self.edits.put_nowait((bstart, bend, b""))

    # syntax highlighting
    istart = bstart
    iend_old = bend
    iend = bstart
    start = get_binary_location_from_binary_index(self.btext, istart)
    end_old = get_binary_location_from_binary_index(self.btext, iend_old)
    end = start
    self.update_tree(istart, iend_old, iend, start, end_old, end)

__aiter__()

Implement the asynchronous iterator protocol.

Source code in src/elva/widgets/textarea.py
def __aiter__(self):
    """
    Implement the asynchronous iterator protocol.
    """
    return self

__anext__() async

Return an edit on the next asynchronous iterator step.

Returns:

  • tuple[int, int, bytes]

    a tuple (itop, ibot, btext) holding the start itop and end ibot indices as well as the UTF-8 encoded text to be inserted at itop.

Source code in src/elva/widgets/textarea.py
async def __anext__(self) -> tuple[int, int, bytes]:
    """
    Return an edit on the next asynchronous iterator step.

    Returns:
        a tuple (itop, ibot, btext) holding the start `itop` and end `ibot` indices as well as the UTF-8 encoded text to be inserted at `itop`.
    """
    return await self.edits.get()

YTextArea(ytext, *args, language=None, **kwargs)

Bases: TextArea

Widget for displaying and manipulating text synchronized in realtime.

Parameters:

  • ytext (Text) –

    Y text data type holding the text.

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

    syntax language the text is written in.

  • args (tuple, default: () ) –

    positional arguments passed to TextArea.

  • kwargs (dict, default: {} ) –

    keyword arguments passed to TextArea.

Methods:

  • on_mount

    Hook called on mounting.

  • update_btext

    Reference the UTF-8 encoded text in an own class attribute.

  • perform_edits

    Task waiting for edits and perform further steps.

  • edit

    Update the widget's visual appearance from an edit's metrics.

  • replace

    Replace a specified range in the text with another text.

  • delete

    Delete a specified range in the text.

  • insert

    Insert text at a specified location.

  • clear

    Clear the document, i.e. delete all text.

  • render_line

    Render a line of content after updating the wrapped text.

Attributes:

Source code in src/elva/widgets/textarea.py
def __init__(
    self, ytext: Text, *args: tuple, language: None | str = None, **kwargs: dict
):
    """
    Arguments:
        ytext: Y text data type holding the text.
        language: syntax language the text is written in.
        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.document = YDocument(ytext, language)
    self.wrapped_document = WrappedDocument(self.document)
    self.navigator = DocumentNavigator(self.wrapped_document)
    self.update_btext()

btext instance-attribute

UTF-8 encoded text.

document = YDocument(ytext, language) instance-attribute

Instance of the document object.

wrapped_document = WrappedDocument(self.document) instance-attribute

Instance of a wrapper class providing wrapping functionality.

navigator = DocumentNavigator(self.wrapped_document) instance-attribute

Instance of a navigator object responsible for proper cursor placement.

on_mount()

Hook called on mounting.

This starts a tasks waiting for edits and updating the widget's visual appearance.

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

    This starts a tasks waiting for edits and updating the widget's visual appearance.
    """
    self.run_worker(self.perform_edits())

update_btext()

Reference the UTF-8 encoded text in an own class attribute.

This makes it possible to calculate metrics before and after a new edit.

Source code in src/elva/widgets/textarea.py
def update_btext(self):
    """
    Reference the UTF-8 encoded text in an own class attribute.

    This makes it possible to calculate metrics before and after a new edit.
    """
    self.btext = self.document.btext

perform_edits() async

Task waiting for edits and perform further steps.

Source code in src/elva/widgets/textarea.py
async def perform_edits(self):
    """
    Task waiting for edits and perform further steps.
    """
    async for itop, ibot, btext in self.document:
        self.edit(itop, ibot, btext)

edit(itop, ibot, btext)

Update the widget's visual appearance from an edit's metrics.

Parameters:

  • itop (int) –

    start index of the edit.

  • ibot (int) –

    end index of the edit.

  • btext (bytes) –

    inserted UTF-8 encoded text.

Source code in src/elva/widgets/textarea.py
def edit(self, itop: int, ibot: int, btext: bytes):
    """
    Update the widget's visual appearance from an edit's metrics.

    Arguments:
        itop: start index of the edit.
        ibot: end index of the edit.
        btext: inserted UTF-8 encoded text.
    """
    # end location of the edit
    iend_edit = itop + len(btext)

    # binary indices for current selection
    # TODO: Save the selection as binary index range as well,
    #       so it does not need to be retrieved from the binary content.
    #       Then, there is no need for a YTextArea.btext attribute anymore;
    #       the history management is already implemented in YDoc
    start_sel, end_sel = self.selection
    start_sel, end_sel = sorted((start_sel, end_sel))  # important!
    istart, iend = [
        get_binary_index_from_location(self.btext, loc)
        for loc in (start_sel, end_sel)
    ]

    # calculate new start and end locations
    ilen = iend - istart

    new_istart = update_location(istart, itop, ibot, iend_edit)
    iend = new_istart + ilen

    if new_istart == istart:
        iend = update_location(iend, itop, ibot, iend_edit)

    istart = new_istart

    # turn binary indices into locations
    self.update_btext()

    start, end = [
        get_location_from_binary_index(self.btext, index)
        for index in (istart, iend)
    ]

    # UI updates
    self.wrapped_document.wrap(self.wrap_width, self.indent_width)

    self._refresh_size()
    self.selection = Selection(start=start, end=end)
    self.record_cursor_width()

    self._build_highlight_map()

    self.post_message(self.Changed(self))

_replace_via_keyboard(insert, start, end)

Perform a replacement only when the text area is not read-only.

Parameters:

  • insert (str) –

    the text to be inserted at start.

  • start (tuple[int, int]) –

    a tuple of row and column coordinates where the replaced text starts.

  • end (tuple[int, int]) –

    a tuple of row and column coordinates where the replaced text ends.

Source code in src/elva/widgets/textarea.py
def _replace_via_keyboard(
    self, insert: str, start: tuple[int, int], end: tuple[int, int]
):
    """
    Perform a replacement only when the text area is not read-only.

    Arguments:
        insert: the text to be inserted at `start`.
        start: a tuple of row and column coordinates where the replaced text starts.
        end: a tuple of row and column coordinates where the replaced text ends.
    """
    if self.read_only:
        return None
    return self.replace(start, end, insert)

_delete_via_keyboard(start, end)

Perform a deletion when the text area is not read-only.

Parameters:

  • start (tuple[int, int]) –

    a tuple of row and column coordinates where the deleted text starts.

  • end (tuple[int, int]) –

    a tuple of row and column coordinates where the deleted text ends.

Source code in src/elva/widgets/textarea.py
def _delete_via_keyboard(self, start: tuple[int, int], end: tuple[int, int]):
    """
    Perform a deletion when the text area is not read-only.

    Arguments:
        start: a tuple of row and column coordinates where the deleted text starts.
        end: a tuple of row and column coordinates where the deleted text ends.
    """
    if self.read_only:
        return None
    return self.delete(start, end)

replace(start, end, text)

Replace a specified range in the text with another text.

Parameters:

  • start (tuple[int, int]) –

    a tuple of row and column coordinates where the replaced text starts.

  • end (tuple[int, int]) –

    a tuple of row and column coordinates where the replaced text ends.

  • text (str) –

    the text to be inserted at start.

Source code in src/elva/widgets/textarea.py
def replace(self, start: tuple[int, int], end: tuple[int, int], text: str):
    """
    Replace a specified range in the text with another text.

    Arguments:
        start: a tuple of row and column coordinates where the replaced text starts.
        end: a tuple of row and column coordinates where the replaced text ends.
        text: the text to be inserted at `start`.
    """
    self.document.replace_range(start, end, text)

delete(start, end)

Delete a specified range in the text.

Parameters:

  • start (tuple[int, int]) –

    a tuple of row and column coordinates where the to be deleted text starts.

  • end (tuple[int, int]) –

    a tuple of row and column coordinates where the to be deleted text ends.

Source code in src/elva/widgets/textarea.py
def delete(self, start: tuple[int, int], end: tuple[int, int]):
    """
    Delete a specified range in the text.

    Arguments:
        start: a tuple of row and column coordinates where the to be deleted text starts.
        end: a tuple of row and column coordinates where the to be deleted text ends.
    """
    self.document.replace_range(start, end, "")

insert(text, location=None)

Insert text at a specified location.

Parameters:

  • text (str) –

    the text to be inserted.

  • location (tuple[int, int], default: None ) –

    a tuple of row and column coordinates where the insertion starts.

Source code in src/elva/widgets/textarea.py
def insert(self, text: str, location: tuple[int, int] = None):
    """
    Insert text at a specified location.

    Arguments:
        text: the text to be inserted.
        location: a tuple of row and column coordinates where the insertion starts.
    """
    if location is None:
        location = self.cursor_location()
    self.document.replace_range(location, location, text)

clear()

Clear the document, i.e. delete all text.

Source code in src/elva/widgets/textarea.py
def clear(self):
    """
    Clear the document, i.e. delete all text.
    """
    self.delete(self.document.start, self.document.end)

render_line(y)

Render a line of content after updating the wrapped text.

Parameters:

  • y (int) –

    row index of the line to be rendered.

Returns:

  • Strip

    the rendered line.

Source code in src/elva/widgets/textarea.py
def render_line(self, y: int) -> Strip:
    """
    Render a line of content after updating the wrapped text.

    Arguments:
        y: row index of the line to be rendered.

    Returns:
        the rendered line.
    """
    # update the cache of wrapped lines
    #
    # TODO: Why is this not done automatically?
    #       Probably we need to update the wrapped lines cache elsewhere.
    self.wrapped_document.wrap(self.size.width)
    return super().render_line(y)

ends_with_newline(text)

Check whether a given text ends with a newline character, i.e. \n or \r.

Parameters:

  • text (str) –

    the text to check.

Returns:

  • bool

    If True, the given text ends with a newline character.

Source code in src/elva/widgets/textarea.py
def ends_with_newline(text: str) -> bool:
    """
    Check whether a given text ends with a newline character, i.e. `\\n` or `\\r`.

    Arguments:
        text: the text to check.

    Returns:
        If `True`, the given text ends with a newline character.
    """
    return text.endswith(tuple(VALID_NEWLINES))

get_lines(text, keepends=False)

Split a text into its lines.

If text is empty or ends with a newline character, an empty line is appended to the list of lines.

Parameters:

  • text (str) –

    the text to split into lines.

  • keepends (bool, default: False ) –

    flag whether to keep the newline characters in the line strings.

Returns:

Source code in src/elva/widgets/textarea.py
def get_lines(text: str, keepends: bool = False) -> list[str]:
    """
    Split a text into its lines.

    If `text` is empty or ends with a newline character, an empty line is appended to the list of lines.

    Arguments:
        text: the text to split into lines.
        keepends: flag whether to keep the newline characters in the line strings.

    Returns:
        a list of lines.
    """
    lines = text.splitlines(keepends=keepends)
    if not lines or ends_with_newline(text):
        lines.append("")
    return lines

get_index_from_binary_index(btext, bindex)

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

Parameters:

  • btext (bytes) –

    UTF-8 encoded data.

  • bindex (int) –

    index in btext.

Returns:

  • int

    index in the UTF-8 decoded form of btext.

Source code in src/elva/widgets/textarea.py
def get_index_from_binary_index(btext: bytes, bindex: int) -> int:
    """
    Convert the index in UTF-8 encoding to character index.

    Arguments:
        btext: UTF-8 encoded data.
        bindex: index in `btext`.

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

get_binary_index_from_index(text, index)

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

Parameters:

  • text (str) –

    string to convert the index on.

  • index (int) –

    index in text.

Returns:

  • int

    index in the UTF-8 encoded form of text.

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

    Arguments:
        text: string to convert the index on.
        index: index in `text`.

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

get_location_from_index(text, index)

Return the 2D row and column coordinates in text at position index.

Parameters:

  • text (str) –

    string where to find the coordinates in.

  • index (int) –

    position in text to convert to coordinates.

Returns:

  • tuple[int, int]

    a tuple (row, col) of row and column position.

Source code in src/elva/widgets/textarea.py
def get_location_from_index(text: str, index: int) -> tuple[int, int]:
    """
    Return the 2D row and column coordinates in `text` at position `index`.

    Arguments:
        text: string where to find the coordinates in.
        index: position in `text` to convert to coordinates.

    Returns:
        a tuple `(row, col)` of row and column position.
    """
    text = text[: index + 1]

    out_of_bounds = index + 1 > len(text)
    ends_with_newline = text.endswith(tuple(VALID_NEWLINES))

    before_newline = not ends_with_newline
    on_newline = ends_with_newline and not out_of_bounds
    after_newline = ends_with_newline and out_of_bounds

    col_off = 0
    if on_newline:
        # only remove trailing newline characters in the last line
        text = text.removesuffix("\n").removesuffix("\r")
        col_off = 1
    elif after_newline or (before_newline and out_of_bounds):
        col_off = 1

    lines = get_lines(text, keepends=True)

    last_line = lines[-1]

    row = len(lines) - 1
    col = len(last_line) - 1 + col_off

    return row, col

get_location_from_binary_index(btext, bindex)

Return the 2D row and column coordinates in the UTF-8 decoded form of btext at position bindex.

Parameters:

  • btext (bytes) –

    UTF-8 encoded string where to find the coordinates in.

  • bindex (int) –

    position in btext to convert to coordinates.

Returns:

  • tuple[int, int]

    a tuple (row, col) of row and column position in the UTF-8 decoded form of btext.

Source code in src/elva/widgets/textarea.py
def get_location_from_binary_index(btext: bytes, bindex: int) -> tuple[int, int]:
    """
    Return the 2D row and column coordinates in the UTF-8 decoded form of `btext` at position `bindex`.

    Arguments:
        btext: UTF-8 encoded string where to find the coordinates in.
        bindex: position in `btext` to convert to coordinates.

    Returns:
        a tuple `(row, col)` of row and column position in the UTF-8 decoded form of `btext`.
    """
    text = btext.decode()
    return get_location_from_index(text, get_index_from_binary_index(btext, bindex))

get_index_from_location(text, location)

Convert 2D row and column coordinates to the index position in text.

Parameters:

  • text (str) –

    the text in which to find the index.

  • location (tuple[int, int]) –

    a tuple of row and column coordinates.

Returns:

  • int

    the index position in text at the given location.

Source code in src/elva/widgets/textarea.py
def get_index_from_location(text: str, location: tuple[int, int]) -> int:
    """
    Convert 2D row and column coordinates to the index position in `text`.

    Arguments:
        text: the text in which to find the index.
        location: a tuple of row and column coordinates.

    Returns:
        the index position in `text` at the given `location`.
    """
    row, col = location

    # be ignorant about the type of newline characters
    lines = get_lines(text, keepends=True)

    # include given row and col indices
    lines = lines[: row + 1]

    last_line = lines[-1].rstrip(NEWLINE_CHARS)

    col_off = 0
    if not last_line or col >= len(last_line):
        col_off = 1

    lines[-1] = last_line[: col + 1]
    index = len("".join(lines)) - 1 + col_off

    return index

get_binary_index_from_location(btext, location)

Convert 2D row and column coordinates to the binary index position in btext.

Parameters:

  • btext (bytes) –

    the UTF-8 encoded text in which to find the binary index.

  • location (tuple[int, int]) –

    a tuple of row and column coordinates.

Returns:

  • int

    the binary index position in btext at the given location.

Source code in src/elva/widgets/textarea.py
def get_binary_index_from_location(btext: bytes, location: tuple[int, int]) -> int:
    """
    Convert 2D row and column coordinates to the binary index position in `btext`.

    Arguments:
        btext: the UTF-8 encoded text in which to find the binary index.
        location: a tuple of row and column coordinates.

    Returns:
        the binary index position in `btext` at the given `location`.
    """
    text = btext.decode()
    index = get_index_from_location(text, location)
    return get_binary_index_from_index(text, index)

get_text_range(text, start, end)

Get the slice in text between a start and an end location.

Parameters:

  • text (str) –

    the text to slice.

  • start (tuple[int, int]) –

    a tuple of row and column coordinates to start the slice at.

  • end (tuple[int, int]) –

    a tuple of row and column coordinates the end the slice at.

Returns:

  • str

    the slice in text between start and end location.

Source code in src/elva/widgets/textarea.py
def get_text_range(text: str, start: tuple[int, int], end: tuple[int, int]) -> str:
    """
    Get the slice in `text` between a `start` and an `end` location.

    Arguments:
        text: the text to slice.
        start: a tuple of row and column coordinates to start the slice at.
        end: a tuple of row and column coordinates the end the slice at.

    Returns:
        the slice in `text` between `start` and `end` location.
    """
    start, end = sorted((start, end))
    istart, iend = [get_index_from_location(text, loc) for loc in (start, end)]

    return text[istart:iend]

get_binary_text_range(btext, start, end)

Get the slice in the UTF-8 encoded btext between a start and an end location.

Parameters:

  • btext (bytes) –

    the UTF-8 encoded text to slice.

  • start (tuple[int, int]) –

    a tuple of row and column coordinates to start the slice at.

  • end (tuple[int, int]) –

    a tuple of row and column coordinates the end the slice at.

Returns:

  • bytes

    the UTF-8 encoded slice in btext between start and end location.

Source code in src/elva/widgets/textarea.py
def get_binary_text_range(
    btext: bytes, start: tuple[int, int], end: tuple[int, int]
) -> bytes:
    """
    Get the slice in the UTF-8 encoded `btext` between a `start` and an `end` location.

    Arguments:
        btext: the UTF-8 encoded text to slice.
        start: a tuple of row and column coordinates to start the slice at.
        end: a tuple of row and column coordinates the end the slice at.

    Returns:
        the UTF-8 encoded slice in `btext` between `start` and `end` location.
    """
    start, end = sorted((start, end))
    bistart, biend = [
        get_binary_index_from_location(btext, loc) for loc in (start, end)
    ]

    return btext[bistart:biend]

update_location(iloc, itop, ibot, iend_edit)

Update a location index with respect to edit metrics.

Parameters:

  • iloc (int) –

    index of the location to be updated.

  • itop (int) –

    index of the start of the edited range.

  • ibot (int) –

    index of the end of the edited range.

  • iend_edit (int) –

    index of the end of the edit's content.

Returns:

  • int

    updated location index.

Source code in src/elva/widgets/textarea.py
def update_location(iloc: int, itop: int, ibot: int, iend_edit: int) -> int:
    """
    Update a location index with respect to edit metrics.

    Arguments:
        iloc: index of the location to be updated.
        itop: index of the start of the edited range.
        ibot: index of the end of the edited range.
        iend_edit: index of the end of the edit's content.

    Returns:
        updated location index.
    """

    # location before top
    loc_top = iloc < itop

    # location between top and bottom
    top_loc_bot = itop <= iloc and iloc <= ibot

    if loc_top:
        pass
    elif top_loc_bot:
        iloc = iend_edit
    else:
        # location after bottom
        ioff = ibot - iloc
        iloc = iend_edit - ioff

    return iloc

get_binary_location_from_binary_index(btext, bindex)

Get the 2D row and column coordinates in UTF-8 encoded text at a given position.

Parameters:

  • btext (bytes) –

    UTF-8 encoded text to find the location in.

  • bindex (int) –

    index position to convert to a location.

Returns:

  • tuple[int, int]

    a tuple (row, column) of row and column coordinates.

Source code in src/elva/widgets/textarea.py
def get_binary_location_from_binary_index(btext: bytes, bindex: int) -> tuple[int, int]:
    """
    Get the 2D row and column coordinates in UTF-8 encoded text at a given position.

    Arguments:
        btext: UTF-8 encoded text to find the location in.
        bindex: index position to convert to a location.

    Returns:
        a tuple (row, column) of row and column coordinates.
    """
    btext = btext[: bindex + 1]
    lines = btext.splitlines(keepends=True)
    if lines:
        if lines[-1]:
            row = len(lines) - 1
            col = len(lines[-1]) - 1
        else:
            row = len(lines)
            col = 0
    else:
        row = 0
        col = 0

    return row, col