Repository: Textualize/textual
File: src/textual/widget.py (Python)

Code before:
            for ancestor in self.ancestors:
                if not isinstance(ancestor, Widget):
                    break
                ancestor._clear_arrangement_cache()

        if repaint:
            self._set_dirty(*regions)
            self._content_width_cache = (None, 0)
            self._content_height_cache = (None, 0)
            self._rich_style_cache.clear()
            self._repaint_required = True

        self.check_idle()
        return self

    def remove(self) -> AwaitRemove:
        """Remove the Widget from the DOM (effectively deleting it).

        Returns:
            An awaitable object that waits for the widget to be removed.
        """

        await_remove = self.app._remove_nodes([self], self.parent)
        return await_remove

    def remove_children(
        self, selector: str | type[QueryType] | None = None
    ) -> AwaitRemove:
        """Remove the children of this Widget from the DOM.

        Args:
            selector: A CSS selector to specify which children to remove.

        Returns:
            An awaitable object that waits for the children to be removed.
        """
        if isinstance(selector, str) or selector is None:
            children_to_remove = self.query(selector)
        else:
            children_to_remove = self.query(selector.__name__)
        await_remove = self.app._remove_nodes(list(children_to_remove), self)
        return await_remove

    @asynccontextmanager
    async def batch(self) -> AsyncGenerator[None, None]:
        """Async context manager that combines widget locking and update batching.

        Use this async context manager whenever you want to acquire the widget lock and
        batch app updates at the same time.

        Example:

Review comment (performance):
This method looks like it applies to children and their descendants? Basically equivalent to `self.query(selector).remove()`, so there doesn't seem much point in this existing.

What we could probably use is a method to remove only direct children, which would better serve the use case of removing and mounting in a batch.



Code after:
            self._layout_required = True
            self._stabilize_scrollbar = None
            for ancestor in self.ancestors:
                if not isinstance(ancestor, Widget):
                    break
                ancestor._clear_arrangement_cache()

        if repaint:
            self._set_dirty(*regions)
            self._content_width_cache = (None, 0)
            self._content_height_cache = (None, 0)
            self._rich_style_cache.clear()
            self._repaint_required = True

        self.check_idle()
        return self

    def remove(self) -> AwaitRemove:
        """Remove the Widget from the DOM (effectively deleting it).

        Returns:
            An awaitable object that waits for the widget to be removed.
        """

        await_remove = self.app._remove_nodes([self], self.parent)
        return await_remove

    def remove_children(self, selector: str | type[QueryType] = "*") -> AwaitRemove:
        """Remove the immediate children of this Widget from the DOM.

        Args:
            selector: A CSS selector to specify which direct children to remove.

        Returns:
            An awaitable object that waits for the direct children to be removed.
        """
        if not isinstance(selector, str):
            selector = selector.__name__
        parsed_selectors = parse_selectors(selector)
        children_to_remove = [
            child for child in self.children if match(parsed_selectors, child)
        ]
        await_remove = self.app._remove_nodes(children_to_remove, self)
        return await_remove

    @asynccontextmanager
    async def batch(self) -> AsyncGenerator[None, None]:
        """Async context manager that combines widget locking and update batching.

        Use this async context manager whenever you want to acquire the widget lock and
        batch app updates at the same time.