base_keymap_picker.rs

  1use std::sync::Arc;
  2
  3use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
  4use gpui::{
  5    actions,
  6    elements::{Element as _, Label},
  7    AppContext, Task, ViewContext,
  8};
  9use picker::{Picker, PickerDelegate, PickerEvent};
 10use settings::{settings_file::SettingsFile, BaseKeymap, Settings};
 11use util::ResultExt;
 12use workspace::Workspace;
 13
 14actions!(welcome, [ToggleBaseKeymapSelector]);
 15
 16pub fn init(cx: &mut AppContext) {
 17    cx.add_action(toggle);
 18    BaseKeymapSelector::init(cx);
 19}
 20
 21pub fn toggle(
 22    workspace: &mut Workspace,
 23    _: &ToggleBaseKeymapSelector,
 24    cx: &mut ViewContext<Workspace>,
 25) {
 26    workspace.toggle_modal(cx, |_, cx| {
 27        cx.add_view(|cx| BaseKeymapSelector::new(BaseKeymapSelectorDelegate::new(cx), cx))
 28    });
 29}
 30
 31pub type BaseKeymapSelector = Picker<BaseKeymapSelectorDelegate>;
 32
 33pub struct BaseKeymapSelectorDelegate {
 34    matches: Vec<StringMatch>,
 35    selected_index: usize,
 36}
 37
 38impl BaseKeymapSelectorDelegate {
 39    fn new(cx: &mut ViewContext<BaseKeymapSelector>) -> Self {
 40        let base = cx.global::<Settings>().base_keymap;
 41        let selected_index = BaseKeymap::OPTIONS
 42            .iter()
 43            .position(|(_, value)| *value == base)
 44            .unwrap_or(0);
 45        Self {
 46            matches: Vec::new(),
 47            selected_index,
 48        }
 49    }
 50}
 51
 52impl PickerDelegate for BaseKeymapSelectorDelegate {
 53    fn placeholder_text(&self) -> Arc<str> {
 54        "Select a base keymap...".into()
 55    }
 56
 57    fn match_count(&self) -> usize {
 58        self.matches.len()
 59    }
 60
 61    fn selected_index(&self) -> usize {
 62        self.selected_index
 63    }
 64
 65    fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<BaseKeymapSelector>) {
 66        self.selected_index = ix;
 67    }
 68
 69    fn update_matches(
 70        &mut self,
 71        query: String,
 72        cx: &mut ViewContext<BaseKeymapSelector>,
 73    ) -> Task<()> {
 74        let background = cx.background().clone();
 75        let candidates = BaseKeymap::names()
 76            .enumerate()
 77            .map(|(id, name)| StringMatchCandidate {
 78                id,
 79                char_bag: name.into(),
 80                string: name.into(),
 81            })
 82            .collect::<Vec<_>>();
 83
 84        cx.spawn(|this, mut cx| async move {
 85            let matches = if query.is_empty() {
 86                candidates
 87                    .into_iter()
 88                    .enumerate()
 89                    .map(|(index, candidate)| StringMatch {
 90                        candidate_id: index,
 91                        string: candidate.string,
 92                        positions: Vec::new(),
 93                        score: 0.0,
 94                    })
 95                    .collect()
 96            } else {
 97                match_strings(
 98                    &candidates,
 99                    &query,
100                    false,
101                    100,
102                    &Default::default(),
103                    background,
104                )
105                .await
106            };
107
108            this.update(&mut cx, |this, _| {
109                let delegate = this.delegate_mut();
110                delegate.matches = matches;
111                delegate.selected_index = delegate
112                    .selected_index
113                    .min(delegate.matches.len().saturating_sub(1));
114            })
115            .log_err();
116        })
117    }
118
119    fn confirm(&mut self, cx: &mut ViewContext<BaseKeymapSelector>) {
120        if let Some(selection) = self.matches.get(self.selected_index) {
121            let base_keymap = BaseKeymap::from_names(&selection.string);
122            SettingsFile::update(cx, move |settings| settings.base_keymap = Some(base_keymap));
123        }
124        cx.emit(PickerEvent::Dismiss);
125    }
126
127    fn dismissed(&mut self, _cx: &mut ViewContext<BaseKeymapSelector>) {}
128
129    fn render_match(
130        &self,
131        ix: usize,
132        mouse_state: &mut gpui::MouseState,
133        selected: bool,
134        cx: &gpui::AppContext,
135    ) -> gpui::AnyElement<Picker<Self>> {
136        let theme = &cx.global::<Settings>().theme;
137        let keymap_match = &self.matches[ix];
138        let style = theme.picker.item.style_for(mouse_state, selected);
139
140        Label::new(keymap_match.string.clone(), style.label.clone())
141            .with_highlights(keymap_match.positions.clone())
142            .contained()
143            .with_style(style.container)
144            .into_any()
145    }
146}