Skip to content

Triple-click in command palette will crash #6308

@TomJGooding

Description

@TomJGooding

To reproduce, run the app below and try triple-clicking the input.

from textual.app import App


class ExampleApp(App):
    def on_mount(self) -> None:
        self.action_command_palette()


if __name__ == "__main__":
    app = ExampleApp()
    app.run()
Traceback
╭──────────────────────────── Traceback (most recent call last) ────────────────────────────╮
│ /home/tom/Projects/textual/src/textual/widget.py:4208 in render_lines                     │
│                                                                                           │
│   4205 │   │   Returns:                                                                   │
│   4206 │   │   │   A list of list of segments.                                            │
│   4207 │   │   """                                                                        │
│ ❱ 4208 │   │   strips = self._styles_cache.render_widget(self, crop)                      │
│   4209 │   │   return strips                                                              │
│   4210 │                                                                                  │
│   4211 │   def get_style_at(self, x: int, y: int) -> Style:                               │
│                                                                                           │
│ ╭────────────────── locals ──────────────────╮                                            │
│ │ crop = Region(x=0, y=0, width=2, height=2) │                                            │
│ │ self = SearchIcon()                        │                                            │
│ ╰────────────────────────────────────────────╯                                            │
│                                                                                           │
│ /home/tom/Projects/textual/src/textual/_styles_cache.py:112 in render_widget              │
│                                                                                           │
│   109 │   │                                                                               │
│   110 │   │   base_background, background = widget.background_colors                      │
│   111 │   │   styles = widget.styles                                                      │
│ ❱ 112 │   │   strips = self.render(                                                       │
│   113 │   │   │   styles,                                                                 │
│   114 │   │   │   widget.region.size,                                                     │
│   115 │   │   │   base_background,                                                        │
│                                                                                           │
│ ╭──────────────────────────────── locals ─────────────────────────────────╮               │
│ │      background = Color(20, 31, 39)                                     │               │
│ │ base_background = Color(20, 31, 39)                                     │               │
│ │ border_subtitle = None                                                  │               │
│ │    border_title = None                                                  │               │
│ │            crop = Region(x=0, y=0, width=2, height=2)                   │               │
│ │            self = <StylesCache dirty={0, 1} width=2>                    │               │
│ │          styles = RenderStyles(                                         │               │
│ │                   │   SearchIcon(),                                     │               │
│ │                   │   auto_color=False,                                 │               │
│ │                   │   color=Color(0, 0, 0),                             │               │
│ │                   │   margin=Spacing(                                   │               │
│ │                   │   │   top=1,                                        │               │
│ │                   │   │   right=0,                                      │               │
│ │                   │   │   bottom=0,                                     │               │
│ │                   │   │   left=1                                        │               │
│ │                   │   ),                                                │               │
│ │                   │   width=Scalar(                                     │               │
│ │                   │   │   value=2.0,                                    │               │
│ │                   │   │   unit=<Unit.CELLS: 1>,                         │               │
│ │                   │   │   percent_unit=<Unit.WIDTH: 4>                  │               │
│ │                   │   )                                                 │               │
│ │                   )                                                     │               │
│ │          widget = SearchIcon()                                          │               │
│ ╰─────────────────────────────────────────────────────────────────────────╯               │
│                                                                                           │
│ /home/tom/Projects/textual/src/textual/_styles_cache.py:214 in render                     │
│                                                                                           │
│   211 │   │                                                                               │
│   212 │   │   for y in crop.line_range:                                                   │
│   213 │   │   │   if is_dirty(y) or y not in self._cache:                                 │
│ ❱ 214 │   │   │   │   strip = render_line(                                                │
│   215 │   │   │   │   │   styles,                                                         │
│   216 │   │   │   │   │   y,                                                              │
│   217 │   │   │   │   │   size,                                                           │
│                                                                                           │
│ ╭─────────────────────────────────────── locals ────────────────────────────────────────╮ │
│ │             _height = 2                                                               │ │
│ │           add_strip = <built-in method append of list object at 0x7f0487455840>       │ │
│ │          ansi_theme = <rich.terminal_theme.TerminalTheme object at 0x7f04896d7ad0>    │ │
│ │          background = Color(20, 31, 39)                                               │ │
│ │     base_background = Color(20, 31, 39)                                               │ │
│ │     border_subtitle = None                                                            │ │
│ │        border_title = None                                                            │ │
│ │        content_size = Size(width=2, height=2)                                         │ │
│ │                crop = Region(x=0, y=0, width=2, height=2)                             │ │
│ │             filters = [<textual.filter.ANSIToTruecolor object at 0x7f0488612a50>]     │ │
│ │            is_dirty = <built-in method __contains__ of set object at 0x7f04885b3d80>  │ │
│ │             opacity = 1.0                                                             │ │
│ │             padding = Spacing(top=0, right=0, bottom=0, left=0)                       │ │
│ │ render_content_line = <bound method Widget.render_line of SearchIcon()>               │ │
│ │         render_line = <bound method StylesCache.render_line of <StylesCache dirty={0, │ │
│ │                       1} width=2>>                                                    │ │
│ │                self = <StylesCache dirty={0, 1} width=2>                              │ │
│ │                size = Size(width=2, height=2)                                         │ │
│ │              strips = []                                                              │ │
│ │              styles = RenderStyles(                                                   │ │
│ │                       │   SearchIcon(),                                               │ │
│ │                       │   auto_color=False,                                           │ │
│ │                       │   color=Color(0, 0, 0),                                       │ │
│ │                       │   margin=Spacing(top=1, right=0, bottom=0, left=1),           │ │
│ │                       │   width=Scalar(                                               │ │
│ │                       │   │   value=2.0,                                              │ │
│ │                       │   │   unit=<Unit.CELLS: 1>,                                   │ │
│ │                       │   │   percent_unit=<Unit.WIDTH: 4>                            │ │
│ │                       │   )                                                           │ │
│ │                       )                                                               │ │
│ │               width = 2                                                               │ │
│ │                   y = 0                                                               │ │
│ ╰───────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                           │
│ /home/tom/Projects/textual/src/textual/_styles_cache.py:441 in render_line                │
│                                                                                           │
│   438 │   │   │   # Content with border and padding (C)                                   │
│   439 │   │   │   content_y = y - gutter.top                                              │
│   440 │   │   │   if content_y < content_height:                                          │
│ ❱ 441 │   │   │   │   line = render_content_line(y - gutter.top)                          │
│   442 │   │   │   │   line = line.adjust_cell_length(content_width, inner.rich_style)     │
│   443 │   │   │   else:                                                                   │
│   444 │   │   │   │   line = Strip.blank(content_width, inner.rich_style)                 │
│                                                                                           │
│ ╭─────────────────────────────────────── locals ────────────────────────────────────────╮ │
│ │           ansi_theme = <rich.terminal_theme.TerminalTheme object at 0x7f04896d7ad0>   │ │
│ │           background = Color(20, 31, 39)                                              │ │
│ │      base_background = Color(20, 31, 39)                                              │ │
│ │        border_bottom = ''                                                             │ │
│ │  border_bottom_color = Color(0, 255, 0)                                               │ │
│ │          border_left = ''                                                             │ │
│ │    border_left_color = Color(0, 255, 0)                                               │ │
│ │         border_right = ''                                                             │ │
│ │   border_right_color = Color(0, 255, 0)                                               │ │
│ │      border_subtitle = None                                                           │ │
│ │         border_title = None                                                           │ │
│ │           border_top = ''                                                             │ │
│ │     border_top_color = Color(0, 255, 0)                                               │ │
│ │       content_height = 2                                                              │ │
│ │         content_size = Size(width=2, height=2)                                        │ │
│ │        content_width = 2                                                              │ │
│ │            content_y = 0                                                              │ │
│ │           from_color = <bound method Style.from_color of <class 'rich.style.Style'>>  │ │
│ │               gutter = Spacing(top=0, right=0, bottom=0, left=0)                      │ │
│ │               height = 2                                                              │ │
│ │                inner = Style(background=Color(20, 31, 39))                            │ │
│ │              opacity = 1.0                                                            │ │
│ │                outer = Style(background=Color(20, 31, 39))                            │ │
│ │       outline_bottom = ''                                                             │ │
│ │ outline_bottom_color = Color(0, 255, 0)                                               │ │
│ │         outline_left = ''                                                             │ │
│ │   outline_left_color = Color(0, 255, 0)                                               │ │
│ │        outline_right = ''                                                             │ │
│ │  outline_right_color = Color(0, 255, 0)                                               │ │
│ │          outline_top = ''                                                             │ │
│ │    outline_top_color = Color(0, 255, 0)                                               │ │
│ │           pad_bottom = 0                                                              │ │
│ │             pad_left = 0                                                              │ │
│ │            pad_right = 0                                                              │ │
│ │              pad_top = 0                                                              │ │
│ │              padding = Spacing(top=0, right=0, bottom=0, left=0)                      │ │
│ │  render_content_line = <bound method Widget.render_line of SearchIcon()>              │ │
│ │                 self = <StylesCache dirty={0, 1} width=2>                             │ │
│ │                 size = Size(width=2, height=2)                                        │ │
│ │               styles = RenderStyles(                                                  │ │
│ │                        │   SearchIcon(),                                              │ │
│ │                        │   auto_color=False,                                          │ │
│ │                        │   color=Color(0, 0, 0),                                      │ │
│ │                        │   margin=Spacing(top=1, right=0, bottom=0, left=1),          │ │
│ │                        │   width=Scalar(                                              │ │
│ │                        │   │   value=2.0,                                             │ │
│ │                        │   │   unit=<Unit.CELLS: 1>,                                  │ │
│ │                        │   │   percent_unit=<Unit.WIDTH: 4>                           │ │
│ │                        │   )                                                          │ │
│ │                        )                                                              │ │
│ │                width = 2                                                              │ │
│ │                    y = 0                                                              │ │
│ ╰───────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                           │
│ /home/tom/Projects/textual/src/textual/widget.py:4191 in render_line                      │
│                                                                                           │
│   4188 │   │   │   A rendered line.                                                       │
│   4189 │   │   """                                                                        │
│   4190 │   │   if self._dirty_regions:                                                    │
│ ❱ 4191 │   │   │   self._render_content()                                                 │
│   4192 │   │   try:                                                                       │
│   4193 │   │   │   line = self._render_cache.lines[y]                                     │
│   4194 │   │   except IndexError:                                                         │
│                                                                                           │
│ ╭────── locals ───────╮                                                                   │
│ │ self = SearchIcon() │                                                                   │
│ │    y = 0            │                                                                   │
│ ╰─────────────────────╯                                                                   │
│                                                                                           │
│ /home/tom/Projects/textual/src/textual/widget.py:4177 in _render_content                  │
│                                                                                           │
│   4174 │   │   """Render all lines."""                                                    │
│   4175 │   │   width, height = self.size                                                  │
│   4176 │   │   visual = self._render()                                                    │
│ ❱ 4177 │   │   strips = Visual.to_strips(self, visual, width, height, self.visual_style)  │
│   4178 │   │   self._render_cache = _RenderCache(self.size, strips)                       │
│   4179 │   │   self._dirty_regions.clear()                                                │
│   4180                                                                                    │
│                                                                                           │
│ ╭──────── locals ────────╮                                                                │
│ │ height = 2             │                                                                │
│ │   self = SearchIcon()  │                                                                │
│ │ visual = Content('🔎') │                                                                │
│ │  width = 2             │                                                                │
│ ╰────────────────────────╯                                                                │
│                                                                                           │
│ /home/tom/Projects/textual/src/textual/visual.py:222 in to_strips                         │
│                                                                                           │
│   219 │   │   selection = widget.text_selection                                           │
│   220 │   │   if selection is not None:                                                   │
│   221 │   │   │   selection_style: Style | None = Style.from_rich_style(                  │
│ ❱ 222 │   │   │   │   widget.screen.get_component_rich_style("screen--selection")         │
│   223 │   │   │   )                                                                       │
│   224 │   │   else:                                                                       │
│   225 │   │   │   selection_style = None                                                  │
│                                                                                           │
│ ╭───────────────────────────────────── locals ─────────────────────────────────────╮      │
│ │ apply_selection = True                                                           │      │
│ │          height = 2                                                              │      │
│ │             pad = False                                                          │      │
│ │      post_style = None                                                           │      │
│ │       selection = Selection(start=None, end=None)                                │      │
│ │           style = Style(background=Color(20, 31, 39), foreground=Color(0, 0, 0)) │      │
│ │          visual = Content('🔎')                                                  │      │
│ │          widget = SearchIcon()                                                   │      │
│ │           width = 2                                                              │      │
│ ╰──────────────────────────────────────────────────────────────────────────────────╯      │
│                                                                                           │
│ /home/tom/Projects/textual/src/textual/widget.py:1151 in get_component_rich_style         │
│                                                                                           │
│   1148 │   │   """                                                                        │
│   1149 │   │                                                                              │
│   1150 │   │   if names not in self._rich_style_cache:                                    │
│ ❱ 1151 │   │   │   component_styles = self.get_component_styles(*names)                   │
│   1152 │   │   │   style = component_styles.rich_style                                    │
│   1153 │   │   │   text_opacity = component_styles.text_opacity                           │
│   1154 │   │   │   if text_opacity < 1 and style.bgcolor is not None:                     │
│                                                                                           │
│ ╭───────────────────────── locals ─────────────────────────╮                              │
│ │   names = ('screen--selection',)                         │                              │
│ │ partial = False                                          │                              │
│ │    self = CommandPalette(                                │                              │
│ │           │   id='--command-palette',                    │                              │
│ │           │   classes='-ready --textual-command-palette' │                              │
│ │           )                                              │                              │
│ ╰──────────────────────────────────────────────────────────╯                              │
│                                                                                           │
│ /home/tom/Projects/textual/src/textual/dom.py:615 in get_component_styles                 │
│                                                                                           │
│    612 │   │                                                                              │
│    613 │   │   for name in names:                                                         │
│    614 │   │   │   if name not in self._component_styles:                                 │
│ ❱  615 │   │   │   │   raise KeyError(f"No {name!r} key in COMPONENT_CLASSES")            │
│    616 │   │   │   component_styles = self._component_styles[name]                        │
│    617 │   │   │   styles.node = component_styles.node                                    │
│    618 │   │   │   styles.base.merge(component_styles.base)                               │
│                                                                                           │
│ ╭────────────────────────── locals ───────────────────────────╮                           │
│ │   name = 'screen--selection'                                │                           │
│ │  names = ('screen--selection',)                             │                           │
│ │   self = CommandPalette(                                    │                           │
│ │          │   id='--command-palette',                        │                           │
│ │          │   classes='-ready --textual-command-palette'     │                           │
│ │          )                                                  │                           │
│ │ styles = RenderStyles(                                      │                           │
│ │          │   CommandPalette(                                │                           │
│ │          │   │   id='--command-palette',                    │                           │
│ │          │   │   classes='-ready --textual-command-palette' │                           │
│ │          │   )                                              │                           │
│ │          )                                                  │                           │
│ ╰─────────────────────────────────────────────────────────────╯                           │
╰───────────────────────────────────────────────────────────────────────────────────────────╯
KeyError: "No 'screen--selection' key in COMPONENT_CLASSES"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions