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}