base_keymap_picker.rs

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