Allow `render_match` to return an `Option` to represent no matches

Marshall Bowers created

Change summary

crates/command_palette2/src/command_palette.rs | 42 ++++++++++---------
crates/file_finder2/src/file_finder.rs         | 30 +++++++------
crates/picker2/src/picker2.rs                  |  4 
crates/storybook2/src/stories/picker.rs        | 28 +++++++------
4 files changed, 55 insertions(+), 49 deletions(-)

Detailed changes

crates/command_palette2/src/command_palette.rs 🔗

@@ -294,32 +294,34 @@ impl PickerDelegate for CommandPaletteDelegate {
         ix: usize,
         selected: bool,
         cx: &mut ViewContext<Picker<Self>>,
-    ) -> Self::ListItem {
+    ) -> Option<Self::ListItem> {
         let colors = cx.theme().colors();
         let Some(r#match) = self.matches.get(ix) else {
-            return div();
+            return None;
         };
         let Some(command) = self.commands.get(r#match.candidate_id) else {
-            return div();
+            return None;
         };
 
-        div()
-            .px_1()
-            .text_color(colors.text)
-            .text_ui()
-            .bg(colors.ghost_element_background)
-            .rounded_md()
-            .when(selected, |this| this.bg(colors.ghost_element_selected))
-            .hover(|this| this.bg(colors.ghost_element_hover))
-            .child(
-                h_stack()
-                    .justify_between()
-                    .child(HighlightedLabel::new(
-                        command.name.clone(),
-                        r#match.positions.clone(),
-                    ))
-                    .children(KeyBinding::for_action(&*command.action, cx)),
-            )
+        Some(
+            div()
+                .px_1()
+                .text_color(colors.text)
+                .text_ui()
+                .bg(colors.ghost_element_background)
+                .rounded_md()
+                .when(selected, |this| this.bg(colors.ghost_element_selected))
+                .hover(|this| this.bg(colors.ghost_element_hover))
+                .child(
+                    h_stack()
+                        .justify_between()
+                        .child(HighlightedLabel::new(
+                            command.name.clone(),
+                            r#match.positions.clone(),
+                        ))
+                        .children(KeyBinding::for_action(&*command.action, cx)),
+                ),
+        )
     }
 }
 

crates/file_finder2/src/file_finder.rs 🔗

@@ -711,7 +711,7 @@ impl PickerDelegate for FileFinderDelegate {
         ix: usize,
         selected: bool,
         cx: &mut ViewContext<Picker<Self>>,
-    ) -> Self::ListItem {
+    ) -> Option<Self::ListItem> {
         let path_match = self
             .matches
             .get(ix)
@@ -722,19 +722,21 @@ impl PickerDelegate for FileFinderDelegate {
         let (file_name, file_name_positions, full_path, full_path_positions) =
             self.labels_for_match(path_match, cx, ix);
 
-        div()
-            .px_1()
-            .text_color(colors.text)
-            .text_ui()
-            .bg(colors.ghost_element_background)
-            .rounded_md()
-            .when(selected, |this| this.bg(colors.ghost_element_selected))
-            .hover(|this| this.bg(colors.ghost_element_hover))
-            .child(
-                v_stack()
-                    .child(HighlightedLabel::new(file_name, file_name_positions))
-                    .child(HighlightedLabel::new(full_path, full_path_positions)),
-            )
+        Some(
+            div()
+                .px_1()
+                .text_color(colors.text)
+                .text_ui()
+                .bg(colors.ghost_element_background)
+                .rounded_md()
+                .when(selected, |this| this.bg(colors.ghost_element_selected))
+                .hover(|this| this.bg(colors.ghost_element_hover))
+                .child(
+                    v_stack()
+                        .child(HighlightedLabel::new(file_name, file_name_positions))
+                        .child(HighlightedLabel::new(full_path, full_path_positions)),
+                ),
+        )
     }
 }
 

crates/picker2/src/picker2.rs 🔗

@@ -32,7 +32,7 @@ pub trait PickerDelegate: Sized + 'static {
         ix: usize,
         selected: bool,
         cx: &mut ViewContext<Picker<Self>>,
-    ) -> Self::ListItem;
+    ) -> Option<Self::ListItem>;
 }
 
 impl<D: PickerDelegate> FocusableView for Picker<D> {
@@ -230,7 +230,7 @@ impl<D: PickerDelegate> Render for Picker<D> {
                                                             )
                                                         }),
                                                     )
-                                                    .child(picker.delegate.render_match(
+                                                    .children(picker.delegate.render_match(
                                                         ix,
                                                         ix == selected_index,
                                                         cx,

crates/storybook2/src/stories/picker.rs 🔗

@@ -51,25 +51,27 @@ impl PickerDelegate for Delegate {
         ix: usize,
         selected: bool,
         cx: &mut gpui::ViewContext<Picker<Self>>,
-    ) -> Self::ListItem {
+    ) -> Option<Self::ListItem> {
         let colors = cx.theme().colors();
         let Some(candidate_ix) = self.matches.get(ix) else {
-            return div();
+            return None;
         };
         // TASK: Make StringMatchCandidate::string a SharedString
         let candidate = SharedString::from(self.candidates[*candidate_ix].string.clone());
 
-        div()
-            .text_color(colors.text)
-            .when(selected, |s| {
-                s.border_l_10().border_color(colors.terminal_ansi_yellow)
-            })
-            .hover(|style| {
-                style
-                    .bg(colors.element_active)
-                    .text_color(colors.text_accent)
-            })
-            .child(candidate)
+        Some(
+            div()
+                .text_color(colors.text)
+                .when(selected, |s| {
+                    s.border_l_10().border_color(colors.terminal_ansi_yellow)
+                })
+                .hover(|style| {
+                    style
+                        .bg(colors.element_active)
+                        .text_color(colors.text_accent)
+                })
+                .child(candidate),
+        )
     }
 
     fn selected_index(&self) -> usize {