We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ListView method to append many
There is a method on ListView to append a single item. We could also use a method to append many items.

If we're following the same conventions as a builtin list, this would be called `ListView.extend` and would accept an Iterable of ListItems.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/textual/widgets/_list_view.py`
Content:
```
1 from __future__ import annotations
2 
3 from typing import ClassVar, Optional
4 
5 from textual.await_remove import AwaitRemove
6 from textual.binding import Binding, BindingType
7 from textual.containers import VerticalScroll
8 from textual.events import Mount
9 from textual.geometry import clamp
10 from textual.message import Message
11 from textual.reactive import reactive
12 from textual.widget import AwaitMount, Widget
13 from textual.widgets._list_item import ListItem
14 
15 
16 class ListView(VerticalScroll, can_focus=True, can_focus_children=False):
17     """A vertical list view widget.
18 
19     Displays a vertical list of `ListItem`s which can be highlighted and
20     selected using the mouse or keyboard.
21 
22     Attributes:
23         index: The index in the list that's currently highlighted.
24     """
25 
26     BINDINGS: ClassVar[list[BindingType]] = [
27         Binding("enter", "select_cursor", "Select", show=False),
28         Binding("up", "cursor_up", "Cursor Up", show=False),
29         Binding("down", "cursor_down", "Cursor Down", show=False),
30     ]
31     """
32     | Key(s) | Description |
33     | :- | :- |
34     | enter | Select the current item. |
35     | up | Move the cursor up. |
36     | down | Move the cursor down. |
37     """
38 
39     index = reactive[Optional[int]](0, always_update=True)
40 
41     class Highlighted(Message, bubble=True):
42         """Posted when the highlighted item changes.
43 
44         Highlighted item is controlled using up/down keys.
45         Can be handled using `on_list_view_highlighted` in a subclass of `ListView`
46         or in a parent widget in the DOM.
47         """
48 
49         ALLOW_SELECTOR_MATCH = {"item"}
50         """Additional message attributes that can be used with the [`on` decorator][textual.on]."""
51 
52         def __init__(self, list_view: ListView, item: ListItem | None) -> None:
53             super().__init__()
54             self.list_view: ListView = list_view
55             """The view that contains the item highlighted."""
56             self.item: ListItem | None = item
57             """The highlighted item, if there is one highlighted."""
58 
59         @property
60         def control(self) -> ListView:
61             """The view that contains the item highlighted.
62 
63             This is an alias for [`Highlighted.list_view`][textual.widgets.ListView.Highlighted.list_view]
64             and is used by the [`on`][textual.on] decorator.
65             """
66             return self.list_view
67 
68     class Selected(Message, bubble=True):
69         """Posted when a list item is selected, e.g. when you press the enter key on it.
70 
71         Can be handled using `on_list_view_selected` in a subclass of `ListView` or in
72         a parent widget in the DOM.
73         """
74 
75         ALLOW_SELECTOR_MATCH = {"item"}
76         """Additional message attributes that can be used with the [`on` decorator][textual.on]."""
77 
78         def __init__(self, list_view: ListView, item: ListItem) -> None:
79             super().__init__()
80             self.list_view: ListView = list_view
81             """The view that contains the item selected."""
82             self.item: ListItem = item
83             """The selected item."""
84 
85         @property
86         def control(self) -> ListView:
87             """The view that contains the item selected.
88 
89             This is an alias for [`Selected.list_view`][textual.widgets.ListView.Selected.list_view]
90             and is used by the [`on`][textual.on] decorator.
91             """
92             return self.list_view
93 
94     def __init__(
95         self,
96         *children: ListItem,
97         initial_index: int | None = 0,
98         name: str | None = None,
99         id: str | None = None,
100         classes: str | None = None,
101         disabled: bool = False,
102     ) -> None:
103         """
104         Initialize a ListView.
105 
106         Args:
107             *children: The ListItems to display in the list.
108             initial_index: The index that should be highlighted when the list is first mounted.
109             name: The name of the widget.
110             id: The unique ID of the widget used in CSS/query selection.
111             classes: The CSS classes of the widget.
112             disabled: Whether the ListView is disabled or not.
113         """
114         super().__init__(
115             *children, name=name, id=id, classes=classes, disabled=disabled
116         )
117         self._index = initial_index
118 
119     def _on_mount(self, _: Mount) -> None:
120         """Ensure the ListView is fully-settled after mounting."""
121         self.index = self._index
122 
123     @property
124     def highlighted_child(self) -> ListItem | None:
125         """The currently highlighted ListItem, or None if nothing is highlighted."""
126         if self.index is not None and 0 <= self.index < len(self._nodes):
127             list_item = self._nodes[self.index]
128             assert isinstance(list_item, ListItem)
129             return list_item
130         else:
131             return None
132 
133     def validate_index(self, index: int | None) -> int | None:
134         """Clamp the index to the valid range, or set to None if there's nothing to highlight.
135 
136         Args:
137             index: The index to clamp.
138 
139         Returns:
140             The clamped index.
141         """
142         if not self._nodes or index is None:
143             return None
144         return self._clamp_index(index)
145 
146     def _clamp_index(self, index: int) -> int:
147         """Clamp the index to a valid value given the current list of children"""
148         last_index = max(len(self._nodes) - 1, 0)
149         return clamp(index, 0, last_index)
150 
151     def _is_valid_index(self, index: int | None) -> bool:
152         """Return True if the current index is valid given the current list of children"""
153         if index is None:
154             return False
155         return 0 <= index < len(self._nodes)
156 
157     def watch_index(self, old_index: int, new_index: int) -> None:
158         """Updates the highlighting when the index changes."""
159         if self._is_valid_index(old_index):
160             old_child = self._nodes[old_index]
161             assert isinstance(old_child, ListItem)
162             old_child.highlighted = False
163 
164         new_child: Widget | None
165         if self._is_valid_index(new_index):
166             new_child = self._nodes[new_index]
167             assert isinstance(new_child, ListItem)
168             new_child.highlighted = True
169         else:
170             new_child = None
171 
172         self._scroll_highlighted_region()
173         self.post_message(self.Highlighted(self, new_child))
174 
175     def append(self, item: ListItem) -> AwaitMount:
176         """Append a new ListItem to the end of the ListView.
177 
178         Args:
179             item: The ListItem to append.
180 
181         Returns:
182             An awaitable that yields control to the event loop
183                 until the DOM has been updated with the new child item.
184         """
185         await_mount = self.mount(item)
186         if len(self) == 1:
187             self.index = 0
188         return await_mount
189 
190     def clear(self) -> AwaitRemove:
191         """Clear all items from the ListView.
192 
193         Returns:
194             An awaitable that yields control to the event loop until
195                 the DOM has been updated to reflect all children being removed.
196         """
197         await_remove = self.query("ListView > ListItem").remove()
198         self.index = None
199         return await_remove
200 
201     def action_select_cursor(self) -> None:
202         """Select the current item in the list."""
203         selected_child = self.highlighted_child
204         if selected_child is None:
205             return
206         self.post_message(self.Selected(self, selected_child))
207 
208     def action_cursor_down(self) -> None:
209         """Highlight the next item in the list."""
210         if self.index is None:
211             self.index = 0
212             return
213         self.index += 1
214 
215     def action_cursor_up(self) -> None:
216         """Highlight the previous item in the list."""
217         if self.index is None:
218             self.index = 0
219             return
220         self.index -= 1
221 
222     def _on_list_item__child_clicked(self, event: ListItem._ChildClicked) -> None:
223         self.focus()
224         self.index = self._nodes.index(event.item)
225         self.post_message(self.Selected(self, event.item))
226 
227     def _scroll_highlighted_region(self) -> None:
228         """Used to keep the highlighted index within vision"""
229         if self.highlighted_child is not None:
230             self.scroll_to_widget(self.highlighted_child, animate=False)
231 
232     def __len__(self):
233         return len(self._nodes)
234
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/textual/widgets/_list_view.py b/src/textual/widgets/_list_view.py
--- a/src/textual/widgets/_list_view.py
+++ b/src/textual/widgets/_list_view.py
@@ -1,6 +1,6 @@
 from __future__ import annotations
 
-from typing import ClassVar, Optional
+from typing import ClassVar, Iterable, Optional
 
 from textual.await_remove import AwaitRemove
 from textual.binding import Binding, BindingType
@@ -172,6 +172,21 @@
         self._scroll_highlighted_region()
         self.post_message(self.Highlighted(self, new_child))
 
+    def extend(self, items: Iterable[ListItem]) -> AwaitMount:
+        """Append multiple new ListItems to the end of the ListView.
+
+        Args:
+            items: The ListItems to append.
+
+        Returns:
+            An awaitable that yields control to the event loop
+                until the DOM has been updated with the new child items.
+        """
+        await_mount = self.mount(*items)
+        if len(self) == 1:
+            self.index = 0
+        return await_mount
+
     def append(self, item: ListItem) -> AwaitMount:
         """Append a new ListItem to the end of the ListView.
 
@@ -182,10 +197,7 @@
             An awaitable that yields control to the event loop
                 until the DOM has been updated with the new child item.
         """
-        await_mount = self.mount(item)
-        if len(self) == 1:
-            self.index = 0
-        return await_mount
+        return self.extend([item])
 
     def clear(self) -> AwaitRemove:
         """Clear all items from the ListView.

{"golden_diff": "diff --git a/src/textual/widgets/_list_view.py b/src/textual/widgets/_list_view.py\n--- a/src/textual/widgets/_list_view.py\n+++ b/src/textual/widgets/_list_view.py\n@@ -1,6 +1,6 @@\n from __future__ import annotations\n \n-from typing import ClassVar, Optional\n+from typing import ClassVar, Iterable, Optional\n \n from textual.await_remove import AwaitRemove\n from textual.binding import Binding, BindingType\n@@ -172,6 +172,21 @@\n         self._scroll_highlighted_region()\n         self.post_message(self.Highlighted(self, new_child))\n \n+    def extend(self, items: Iterable[ListItem]) -> AwaitMount:\n+        \"\"\"Append multiple new ListItems to the end of the ListView.\n+\n+        Args:\n+            items: The ListItems to append.\n+\n+        Returns:\n+            An awaitable that yields control to the event loop\n+                until the DOM has been updated with the new child items.\n+        \"\"\"\n+        await_mount = self.mount(*items)\n+        if len(self) == 1:\n+            self.index = 0\n+        return await_mount\n+\n     def append(self, item: ListItem) -> AwaitMount:\n         \"\"\"Append a new ListItem to the end of the ListView.\n \n@@ -182,10 +197,7 @@\n             An awaitable that yields control to the event loop\n                 until the DOM has been updated with the new child item.\n         \"\"\"\n-        await_mount = self.mount(item)\n-        if len(self) == 1:\n-            self.index = 0\n-        return await_mount\n+        return self.extend([item])\n \n     def clear(self) -> AwaitRemove:\n         \"\"\"Clear all items from the ListView.\n", "issue": "ListView method to append many\nThere is a method on ListView to append a single item. We could also use a method to append many items.\n\nIf we're following the same conventions as a builtin list, this would be called `ListView.extend` and would accept an Iterable of ListItems.\n", "before_files": [{"content": "from __future__ import annotations\n\nfrom typing import ClassVar, Optional\n\nfrom textual.await_remove import AwaitRemove\nfrom textual.binding import Binding, BindingType\nfrom textual.containers import VerticalScroll\nfrom textual.events import Mount\nfrom textual.geometry import clamp\nfrom textual.message import Message\nfrom textual.reactive import reactive\nfrom textual.widget import AwaitMount, Widget\nfrom textual.widgets._list_item import ListItem\n\n\nclass ListView(VerticalScroll, can_focus=True, can_focus_children=False):\n    \"\"\"A vertical list view widget.\n\n    Displays a vertical list of `ListItem`s which can be highlighted and\n    selected using the mouse or keyboard.\n\n    Attributes:\n        index: The index in the list that's currently highlighted.\n    \"\"\"\n\n    BINDINGS: ClassVar[list[BindingType]] = [\n        Binding(\"enter\", \"select_cursor\", \"Select\", show=False),\n        Binding(\"up\", \"cursor_up\", \"Cursor Up\", show=False),\n        Binding(\"down\", \"cursor_down\", \"Cursor Down\", show=False),\n    ]\n    \"\"\"\n    | Key(s) | Description |\n    | :- | :- |\n    | enter | Select the current item. |\n    | up | Move the cursor up. |\n    | down | Move the cursor down. |\n    \"\"\"\n\n    index = reactive[Optional[int]](0, always_update=True)\n\n    class Highlighted(Message, bubble=True):\n        \"\"\"Posted when the highlighted item changes.\n\n        Highlighted item is controlled using up/down keys.\n        Can be handled using `on_list_view_highlighted` in a subclass of `ListView`\n        or in a parent widget in the DOM.\n        \"\"\"\n\n        ALLOW_SELECTOR_MATCH = {\"item\"}\n        \"\"\"Additional message attributes that can be used with the [`on` decorator][textual.on].\"\"\"\n\n        def __init__(self, list_view: ListView, item: ListItem | None) -> None:\n            super().__init__()\n            self.list_view: ListView = list_view\n            \"\"\"The view that contains the item highlighted.\"\"\"\n            self.item: ListItem | None = item\n            \"\"\"The highlighted item, if there is one highlighted.\"\"\"\n\n        @property\n        def control(self) -> ListView:\n            \"\"\"The view that contains the item highlighted.\n\n            This is an alias for [`Highlighted.list_view`][textual.widgets.ListView.Highlighted.list_view]\n            and is used by the [`on`][textual.on] decorator.\n            \"\"\"\n            return self.list_view\n\n    class Selected(Message, bubble=True):\n        \"\"\"Posted when a list item is selected, e.g. when you press the enter key on it.\n\n        Can be handled using `on_list_view_selected` in a subclass of `ListView` or in\n        a parent widget in the DOM.\n        \"\"\"\n\n        ALLOW_SELECTOR_MATCH = {\"item\"}\n        \"\"\"Additional message attributes that can be used with the [`on` decorator][textual.on].\"\"\"\n\n        def __init__(self, list_view: ListView, item: ListItem) -> None:\n            super().__init__()\n            self.list_view: ListView = list_view\n            \"\"\"The view that contains the item selected.\"\"\"\n            self.item: ListItem = item\n            \"\"\"The selected item.\"\"\"\n\n        @property\n        def control(self) -> ListView:\n            \"\"\"The view that contains the item selected.\n\n            This is an alias for [`Selected.list_view`][textual.widgets.ListView.Selected.list_view]\n            and is used by the [`on`][textual.on] decorator.\n            \"\"\"\n            return self.list_view\n\n    def __init__(\n        self,\n        *children: ListItem,\n        initial_index: int | None = 0,\n        name: str | None = None,\n        id: str | None = None,\n        classes: str | None = None,\n        disabled: bool = False,\n    ) -> None:\n        \"\"\"\n        Initialize a ListView.\n\n        Args:\n            *children: The ListItems to display in the list.\n            initial_index: The index that should be highlighted when the list is first mounted.\n            name: The name of the widget.\n            id: The unique ID of the widget used in CSS/query selection.\n            classes: The CSS classes of the widget.\n            disabled: Whether the ListView is disabled or not.\n        \"\"\"\n        super().__init__(\n            *children, name=name, id=id, classes=classes, disabled=disabled\n        )\n        self._index = initial_index\n\n    def _on_mount(self, _: Mount) -> None:\n        \"\"\"Ensure the ListView is fully-settled after mounting.\"\"\"\n        self.index = self._index\n\n    @property\n    def highlighted_child(self) -> ListItem | None:\n        \"\"\"The currently highlighted ListItem, or None if nothing is highlighted.\"\"\"\n        if self.index is not None and 0 <= self.index < len(self._nodes):\n            list_item = self._nodes[self.index]\n            assert isinstance(list_item, ListItem)\n            return list_item\n        else:\n            return None\n\n    def validate_index(self, index: int | None) -> int | None:\n        \"\"\"Clamp the index to the valid range, or set to None if there's nothing to highlight.\n\n        Args:\n            index: The index to clamp.\n\n        Returns:\n            The clamped index.\n        \"\"\"\n        if not self._nodes or index is None:\n            return None\n        return self._clamp_index(index)\n\n    def _clamp_index(self, index: int) -> int:\n        \"\"\"Clamp the index to a valid value given the current list of children\"\"\"\n        last_index = max(len(self._nodes) - 1, 0)\n        return clamp(index, 0, last_index)\n\n    def _is_valid_index(self, index: int | None) -> bool:\n        \"\"\"Return True if the current index is valid given the current list of children\"\"\"\n        if index is None:\n            return False\n        return 0 <= index < len(self._nodes)\n\n    def watch_index(self, old_index: int, new_index: int) -> None:\n        \"\"\"Updates the highlighting when the index changes.\"\"\"\n        if self._is_valid_index(old_index):\n            old_child = self._nodes[old_index]\n            assert isinstance(old_child, ListItem)\n            old_child.highlighted = False\n\n        new_child: Widget | None\n        if self._is_valid_index(new_index):\n            new_child = self._nodes[new_index]\n            assert isinstance(new_child, ListItem)\n            new_child.highlighted = True\n        else:\n            new_child = None\n\n        self._scroll_highlighted_region()\n        self.post_message(self.Highlighted(self, new_child))\n\n    def append(self, item: ListItem) -> AwaitMount:\n        \"\"\"Append a new ListItem to the end of the ListView.\n\n        Args:\n            item: The ListItem to append.\n\n        Returns:\n            An awaitable that yields control to the event loop\n                until the DOM has been updated with the new child item.\n        \"\"\"\n        await_mount = self.mount(item)\n        if len(self) == 1:\n            self.index = 0\n        return await_mount\n\n    def clear(self) -> AwaitRemove:\n        \"\"\"Clear all items from the ListView.\n\n        Returns:\n            An awaitable that yields control to the event loop until\n                the DOM has been updated to reflect all children being removed.\n        \"\"\"\n        await_remove = self.query(\"ListView > ListItem\").remove()\n        self.index = None\n        return await_remove\n\n    def action_select_cursor(self) -> None:\n        \"\"\"Select the current item in the list.\"\"\"\n        selected_child = self.highlighted_child\n        if selected_child is None:\n            return\n        self.post_message(self.Selected(self, selected_child))\n\n    def action_cursor_down(self) -> None:\n        \"\"\"Highlight the next item in the list.\"\"\"\n        if self.index is None:\n            self.index = 0\n            return\n        self.index += 1\n\n    def action_cursor_up(self) -> None:\n        \"\"\"Highlight the previous item in the list.\"\"\"\n        if self.index is None:\n            self.index = 0\n            return\n        self.index -= 1\n\n    def _on_list_item__child_clicked(self, event: ListItem._ChildClicked) -> None:\n        self.focus()\n        self.index = self._nodes.index(event.item)\n        self.post_message(self.Selected(self, event.item))\n\n    def _scroll_highlighted_region(self) -> None:\n        \"\"\"Used to keep the highlighted index within vision\"\"\"\n        if self.highlighted_child is not None:\n            self.scroll_to_widget(self.highlighted_child, animate=False)\n\n    def __len__(self):\n        return len(self._nodes)\n", "path": "src/textual/widgets/_list_view.py"}], "after_files": [{"content": "from __future__ import annotations\n\nfrom typing import ClassVar, Iterable, Optional\n\nfrom textual.await_remove import AwaitRemove\nfrom textual.binding import Binding, BindingType\nfrom textual.containers import VerticalScroll\nfrom textual.events import Mount\nfrom textual.geometry import clamp\nfrom textual.message import Message\nfrom textual.reactive import reactive\nfrom textual.widget import AwaitMount, Widget\nfrom textual.widgets._list_item import ListItem\n\n\nclass ListView(VerticalScroll, can_focus=True, can_focus_children=False):\n    \"\"\"A vertical list view widget.\n\n    Displays a vertical list of `ListItem`s which can be highlighted and\n    selected using the mouse or keyboard.\n\n    Attributes:\n        index: The index in the list that's currently highlighted.\n    \"\"\"\n\n    BINDINGS: ClassVar[list[BindingType]] = [\n        Binding(\"enter\", \"select_cursor\", \"Select\", show=False),\n        Binding(\"up\", \"cursor_up\", \"Cursor Up\", show=False),\n        Binding(\"down\", \"cursor_down\", \"Cursor Down\", show=False),\n    ]\n    \"\"\"\n    | Key(s) | Description |\n    | :- | :- |\n    | enter | Select the current item. |\n    | up | Move the cursor up. |\n    | down | Move the cursor down. |\n    \"\"\"\n\n    index = reactive[Optional[int]](0, always_update=True)\n\n    class Highlighted(Message, bubble=True):\n        \"\"\"Posted when the highlighted item changes.\n\n        Highlighted item is controlled using up/down keys.\n        Can be handled using `on_list_view_highlighted` in a subclass of `ListView`\n        or in a parent widget in the DOM.\n        \"\"\"\n\n        ALLOW_SELECTOR_MATCH = {\"item\"}\n        \"\"\"Additional message attributes that can be used with the [`on` decorator][textual.on].\"\"\"\n\n        def __init__(self, list_view: ListView, item: ListItem | None) -> None:\n            super().__init__()\n            self.list_view: ListView = list_view\n            \"\"\"The view that contains the item highlighted.\"\"\"\n            self.item: ListItem | None = item\n            \"\"\"The highlighted item, if there is one highlighted.\"\"\"\n\n        @property\n        def control(self) -> ListView:\n            \"\"\"The view that contains the item highlighted.\n\n            This is an alias for [`Highlighted.list_view`][textual.widgets.ListView.Highlighted.list_view]\n            and is used by the [`on`][textual.on] decorator.\n            \"\"\"\n            return self.list_view\n\n    class Selected(Message, bubble=True):\n        \"\"\"Posted when a list item is selected, e.g. when you press the enter key on it.\n\n        Can be handled using `on_list_view_selected` in a subclass of `ListView` or in\n        a parent widget in the DOM.\n        \"\"\"\n\n        ALLOW_SELECTOR_MATCH = {\"item\"}\n        \"\"\"Additional message attributes that can be used with the [`on` decorator][textual.on].\"\"\"\n\n        def __init__(self, list_view: ListView, item: ListItem) -> None:\n            super().__init__()\n            self.list_view: ListView = list_view\n            \"\"\"The view that contains the item selected.\"\"\"\n            self.item: ListItem = item\n            \"\"\"The selected item.\"\"\"\n\n        @property\n        def control(self) -> ListView:\n            \"\"\"The view that contains the item selected.\n\n            This is an alias for [`Selected.list_view`][textual.widgets.ListView.Selected.list_view]\n            and is used by the [`on`][textual.on] decorator.\n            \"\"\"\n            return self.list_view\n\n    def __init__(\n        self,\n        *children: ListItem,\n        initial_index: int | None = 0,\n        name: str | None = None,\n        id: str | None = None,\n        classes: str | None = None,\n        disabled: bool = False,\n    ) -> None:\n        \"\"\"\n        Initialize a ListView.\n\n        Args:\n            *children: The ListItems to display in the list.\n            initial_index: The index that should be highlighted when the list is first mounted.\n            name: The name of the widget.\n            id: The unique ID of the widget used in CSS/query selection.\n            classes: The CSS classes of the widget.\n            disabled: Whether the ListView is disabled or not.\n        \"\"\"\n        super().__init__(\n            *children, name=name, id=id, classes=classes, disabled=disabled\n        )\n        self._index = initial_index\n\n    def _on_mount(self, _: Mount) -> None:\n        \"\"\"Ensure the ListView is fully-settled after mounting.\"\"\"\n        self.index = self._index\n\n    @property\n    def highlighted_child(self) -> ListItem | None:\n        \"\"\"The currently highlighted ListItem, or None if nothing is highlighted.\"\"\"\n        if self.index is not None and 0 <= self.index < len(self._nodes):\n            list_item = self._nodes[self.index]\n            assert isinstance(list_item, ListItem)\n            return list_item\n        else:\n            return None\n\n    def validate_index(self, index: int | None) -> int | None:\n        \"\"\"Clamp the index to the valid range, or set to None if there's nothing to highlight.\n\n        Args:\n            index: The index to clamp.\n\n        Returns:\n            The clamped index.\n        \"\"\"\n        if not self._nodes or index is None:\n            return None\n        return self._clamp_index(index)\n\n    def _clamp_index(self, index: int) -> int:\n        \"\"\"Clamp the index to a valid value given the current list of children\"\"\"\n        last_index = max(len(self._nodes) - 1, 0)\n        return clamp(index, 0, last_index)\n\n    def _is_valid_index(self, index: int | None) -> bool:\n        \"\"\"Return True if the current index is valid given the current list of children\"\"\"\n        if index is None:\n            return False\n        return 0 <= index < len(self._nodes)\n\n    def watch_index(self, old_index: int, new_index: int) -> None:\n        \"\"\"Updates the highlighting when the index changes.\"\"\"\n        if self._is_valid_index(old_index):\n            old_child = self._nodes[old_index]\n            assert isinstance(old_child, ListItem)\n            old_child.highlighted = False\n\n        new_child: Widget | None\n        if self._is_valid_index(new_index):\n            new_child = self._nodes[new_index]\n            assert isinstance(new_child, ListItem)\n            new_child.highlighted = True\n        else:\n            new_child = None\n\n        self._scroll_highlighted_region()\n        self.post_message(self.Highlighted(self, new_child))\n\n    def extend(self, items: Iterable[ListItem]) -> AwaitMount:\n        \"\"\"Append multiple new ListItems to the end of the ListView.\n\n        Args:\n            items: The ListItems to append.\n\n        Returns:\n            An awaitable that yields control to the event loop\n                until the DOM has been updated with the new child items.\n        \"\"\"\n        await_mount = self.mount(*items)\n        if len(self) == 1:\n            self.index = 0\n        return await_mount\n\n    def append(self, item: ListItem) -> AwaitMount:\n        \"\"\"Append a new ListItem to the end of the ListView.\n\n        Args:\n            item: The ListItem to append.\n\n        Returns:\n            An awaitable that yields control to the event loop\n                until the DOM has been updated with the new child item.\n        \"\"\"\n        return self.extend([item])\n\n    def clear(self) -> AwaitRemove:\n        \"\"\"Clear all items from the ListView.\n\n        Returns:\n            An awaitable that yields control to the event loop until\n                the DOM has been updated to reflect all children being removed.\n        \"\"\"\n        await_remove = self.query(\"ListView > ListItem\").remove()\n        self.index = None\n        return await_remove\n\n    def action_select_cursor(self) -> None:\n        \"\"\"Select the current item in the list.\"\"\"\n        selected_child = self.highlighted_child\n        if selected_child is None:\n            return\n        self.post_message(self.Selected(self, selected_child))\n\n    def action_cursor_down(self) -> None:\n        \"\"\"Highlight the next item in the list.\"\"\"\n        if self.index is None:\n            self.index = 0\n            return\n        self.index += 1\n\n    def action_cursor_up(self) -> None:\n        \"\"\"Highlight the previous item in the list.\"\"\"\n        if self.index is None:\n            self.index = 0\n            return\n        self.index -= 1\n\n    def _on_list_item__child_clicked(self, event: ListItem._ChildClicked) -> None:\n        self.focus()\n        self.index = self._nodes.index(event.item)\n        self.post_message(self.Selected(self, event.item))\n\n    def _scroll_highlighted_region(self) -> None:\n        \"\"\"Used to keep the highlighted index within vision\"\"\"\n        if self.highlighted_child is not None:\n            self.scroll_to_widget(self.highlighted_child, animate=False)\n\n    def __len__(self):\n        return len(self._nodes)\n", "path": "src/textual/widgets/_list_view.py"}]}