base_keymap_picker.rs

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