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