@@ -6,9 +6,12 @@ use gpui::{
WeakView, WindowContext,
};
use picker::{Picker, PickerDelegate};
-use std::cmp::{self, Reverse};
+use std::{
+ cmp::{self, Reverse},
+ sync::Arc,
+};
use theme::ActiveTheme;
-use ui::{v_stack, Label, StyledExt};
+use ui::{v_stack, HighlightedLabel, StyledExt};
use util::{
channel::{parse_zed_link, ReleaseChannel, RELEASE_CHANNEL},
ResultExt,
@@ -147,6 +150,10 @@ impl CommandPaletteDelegate {
impl PickerDelegate for CommandPaletteDelegate {
type ListItem = Div<Picker<Self>>;
+ fn placeholder_text(&self) -> Arc<str> {
+ "Execute a command...".into()
+ }
+
fn match_count(&self) -> usize {
self.matches.len()
}
@@ -296,11 +303,10 @@ impl PickerDelegate for CommandPaletteDelegate {
cx: &mut ViewContext<Picker<Self>>,
) -> Self::ListItem {
let colors = cx.theme().colors();
- let Some(command) = self
- .matches
- .get(ix)
- .and_then(|m| self.commands.get(m.candidate_id))
- else {
+ let Some(r#match) = self.matches.get(ix) else {
+ return div();
+ };
+ let Some(command) = self.commands.get(r#match.candidate_id) else {
return div();
};
@@ -312,7 +318,10 @@ impl PickerDelegate for CommandPaletteDelegate {
.rounded_md()
.when(selected, |this| this.bg(colors.ghost_element_selected))
.hover(|this| this.bg(colors.ghost_element_hover))
- .child(Label::new(command.name.clone()))
+ .child(HighlightedLabel::new(
+ command.name.clone(),
+ r#match.positions.clone(),
+ ))
}
// fn render_match(
@@ -4,8 +4,8 @@ use gpui::{
StatelessInteractive, Styled, Task, UniformListScrollHandle, View, ViewContext, VisualContext,
WindowContext,
};
-use std::cmp;
-use ui::{prelude::*, v_stack, Divider};
+use std::{cmp, sync::Arc};
+use ui::{prelude::*, v_stack, Divider, Label, LabelColor};
pub struct Picker<D: PickerDelegate> {
pub delegate: D,
@@ -21,7 +21,7 @@ pub trait PickerDelegate: Sized + 'static {
fn selected_index(&self) -> usize;
fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>);
- // fn placeholder_text(&self) -> Arc<str>;
+ fn placeholder_text(&self) -> Arc<str>;
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()>;
fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<Picker<Self>>);
@@ -37,7 +37,11 @@ pub trait PickerDelegate: Sized + 'static {
impl<D: PickerDelegate> Picker<D> {
pub fn new(delegate: D, cx: &mut ViewContext<Self>) -> Self {
- let editor = cx.build_view(|cx| Editor::single_line(cx));
+ let editor = cx.build_view(|cx| {
+ let mut editor = Editor::single_line(cx);
+ editor.set_placeholder_text(delegate.placeholder_text(), cx);
+ editor
+ });
cx.subscribe(&editor, Self::on_input_editor_event).detach();
Self {
delegate,
@@ -159,23 +163,35 @@ impl<D: PickerDelegate> Render for Picker<D> {
.child(div().px_1().py_0p5().child(self.editor.clone())),
)
.child(Divider::horizontal())
- .child(
- v_stack()
- .p_1()
- .grow()
- .child(
- uniform_list("candidates", self.delegate.match_count(), {
- move |this: &mut Self, visible_range, cx| {
- let selected_ix = this.delegate.selected_index();
- visible_range
- .map(|ix| this.delegate.render_match(ix, ix == selected_ix, cx))
- .collect()
- }
- })
- .track_scroll(self.scroll_handle.clone()),
- )
- .max_h_72()
- .overflow_hidden(),
- )
+ .when(self.delegate.match_count() > 0, |el| {
+ el.child(
+ v_stack()
+ .p_1()
+ .grow()
+ .child(
+ uniform_list("candidates", self.delegate.match_count(), {
+ move |this: &mut Self, visible_range, cx| {
+ let selected_ix = this.delegate.selected_index();
+ visible_range
+ .map(|ix| {
+ this.delegate.render_match(ix, ix == selected_ix, cx)
+ })
+ .collect()
+ }
+ })
+ .track_scroll(self.scroll_handle.clone()),
+ )
+ .max_h_72()
+ .overflow_hidden(),
+ )
+ })
+ .when(self.delegate.match_count() == 0, |el| {
+ el.child(
+ v_stack()
+ .p_1()
+ .grow()
+ .child(Label::new("No matches").color(LabelColor::Muted)),
+ )
+ })
}
}