base_keymap_picker.rs

  1use super::base_keymap_setting::BaseKeymap;
  2use client::telemetry::Telemetry;
  3use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
  4use gpui::{
  5    actions, AppContext, DismissEvent, EventEmitter, FocusableView, Render, Task, View,
  6    ViewContext, VisualContext, WeakView,
  7};
  8use picker::{Picker, PickerDelegate};
  9use project::Fs;
 10use settings::{update_settings_file, Settings};
 11use std::sync::Arc;
 12use ui::{prelude::*, ListItem, ListItemSpacing};
 13use util::ResultExt;
 14use workspace::{ui::HighlightedLabel, ModalView, Workspace};
 15
 16actions!(welcome, [ToggleBaseKeymapSelector]);
 17
 18pub fn init(cx: &mut AppContext) {
 19    cx.observe_new_views(|workspace: &mut Workspace, _cx| {
 20        workspace.register_action(toggle);
 21    })
 22    .detach();
 23}
 24
 25pub fn toggle(
 26    workspace: &mut Workspace,
 27    _: &ToggleBaseKeymapSelector,
 28    cx: &mut ViewContext<Workspace>,
 29) {
 30    let fs = workspace.app_state().fs.clone();
 31    let telemetry = workspace.client().telemetry().clone();
 32    workspace.toggle_modal(cx, |cx| {
 33        BaseKeymapSelector::new(
 34            BaseKeymapSelectorDelegate::new(cx.view().downgrade(), fs, telemetry, cx),
 35            cx,
 36        )
 37    });
 38}
 39
 40pub struct BaseKeymapSelector {
 41    focus_handle: gpui::FocusHandle,
 42    picker: View<Picker<BaseKeymapSelectorDelegate>>,
 43}
 44
 45impl FocusableView for BaseKeymapSelector {
 46    fn focus_handle(&self, _cx: &AppContext) -> gpui::FocusHandle {
 47        self.focus_handle.clone()
 48    }
 49}
 50
 51impl EventEmitter<DismissEvent> for BaseKeymapSelector {}
 52impl ModalView for BaseKeymapSelector {}
 53
 54impl BaseKeymapSelector {
 55    pub fn new(
 56        delegate: BaseKeymapSelectorDelegate,
 57        cx: &mut ViewContext<BaseKeymapSelector>,
 58    ) -> Self {
 59        let picker = cx.new_view(|cx| Picker::new(delegate, cx));
 60        let focus_handle = cx.focus_handle();
 61        Self {
 62            focus_handle,
 63            picker,
 64        }
 65    }
 66}
 67
 68impl Render for BaseKeymapSelector {
 69    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
 70        self.picker.clone()
 71    }
 72}
 73
 74pub struct BaseKeymapSelectorDelegate {
 75    view: WeakView<BaseKeymapSelector>,
 76    matches: Vec<StringMatch>,
 77    selected_index: usize,
 78    telemetry: Arc<Telemetry>,
 79    fs: Arc<dyn Fs>,
 80}
 81
 82impl BaseKeymapSelectorDelegate {
 83    fn new(
 84        weak_view: WeakView<BaseKeymapSelector>,
 85        fs: Arc<dyn Fs>,
 86        telemetry: Arc<Telemetry>,
 87        cx: &mut ViewContext<BaseKeymapSelector>,
 88    ) -> Self {
 89        let base = BaseKeymap::get(None, cx);
 90        let selected_index = BaseKeymap::OPTIONS
 91            .iter()
 92            .position(|(_, value)| value == base)
 93            .unwrap_or(0);
 94        Self {
 95            view: weak_view,
 96            matches: Vec::new(),
 97            selected_index,
 98            telemetry,
 99            fs,
100        }
101    }
102}
103
104impl PickerDelegate for BaseKeymapSelectorDelegate {
105    type ListItem = ui::ListItem;
106
107    fn placeholder_text(&self) -> Arc<str> {
108        "Select a base keymap...".into()
109    }
110
111    fn match_count(&self) -> usize {
112        self.matches.len()
113    }
114
115    fn selected_index(&self) -> usize {
116        self.selected_index
117    }
118
119    fn set_selected_index(
120        &mut self,
121        ix: usize,
122        _: &mut ViewContext<Picker<BaseKeymapSelectorDelegate>>,
123    ) {
124        self.selected_index = ix;
125    }
126
127    fn update_matches(
128        &mut self,
129        query: String,
130        cx: &mut ViewContext<Picker<BaseKeymapSelectorDelegate>>,
131    ) -> Task<()> {
132        let background = cx.background_executor().clone();
133        let candidates = BaseKeymap::names()
134            .enumerate()
135            .map(|(id, name)| StringMatchCandidate {
136                id,
137                char_bag: name.into(),
138                string: name.into(),
139            })
140            .collect::<Vec<_>>();
141
142        cx.spawn(|this, mut cx| async move {
143            let matches = if query.is_empty() {
144                candidates
145                    .into_iter()
146                    .enumerate()
147                    .map(|(index, candidate)| StringMatch {
148                        candidate_id: index,
149                        string: candidate.string,
150                        positions: Vec::new(),
151                        score: 0.0,
152                    })
153                    .collect()
154            } else {
155                match_strings(
156                    &candidates,
157                    &query,
158                    false,
159                    100,
160                    &Default::default(),
161                    background,
162                )
163                .await
164            };
165
166            this.update(&mut cx, |this, _| {
167                this.delegate.matches = matches;
168                this.delegate.selected_index = this
169                    .delegate
170                    .selected_index
171                    .min(this.delegate.matches.len().saturating_sub(1));
172            })
173            .log_err();
174        })
175    }
176
177    fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<BaseKeymapSelectorDelegate>>) {
178        if let Some(selection) = self.matches.get(self.selected_index) {
179            let base_keymap = BaseKeymap::from_names(&selection.string);
180
181            self.telemetry
182                .report_setting_event("keymap", base_keymap.to_string());
183
184            update_settings_file::<BaseKeymap>(self.fs.clone(), cx, move |setting| {
185                *setting = Some(base_keymap)
186            });
187        }
188
189        self.view
190            .update(cx, |_, cx| {
191                cx.emit(DismissEvent);
192            })
193            .ok();
194    }
195
196    fn dismissed(&mut self, _cx: &mut ViewContext<Picker<BaseKeymapSelectorDelegate>>) {}
197
198    fn render_match(
199        &self,
200        ix: usize,
201        selected: bool,
202        _cx: &mut gpui::ViewContext<Picker<Self>>,
203    ) -> Option<Self::ListItem> {
204        let keymap_match = &self.matches[ix];
205
206        Some(
207            ListItem::new(ix)
208                .inset(true)
209                .spacing(ListItemSpacing::Sparse)
210                .selected(selected)
211                .child(HighlightedLabel::new(
212                    keymap_match.string.clone(),
213                    keymap_match.positions.clone(),
214                )),
215        )
216    }
217}