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::uniform_list(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_flex().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, _cx: &mut WindowContext) -> 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::new(id, name))
126 .collect::<Vec<_>>();
127
128 cx.spawn(|this, mut cx| async move {
129 let matches = if query.is_empty() {
130 candidates
131 .into_iter()
132 .enumerate()
133 .map(|(index, candidate)| StringMatch {
134 candidate_id: index,
135 string: candidate.string,
136 positions: Vec::new(),
137 score: 0.0,
138 })
139 .collect()
140 } else {
141 match_strings(
142 &candidates,
143 &query,
144 false,
145 100,
146 &Default::default(),
147 background,
148 )
149 .await
150 };
151
152 this.update(&mut cx, |this, _| {
153 this.delegate.matches = matches;
154 this.delegate.selected_index = this
155 .delegate
156 .selected_index
157 .min(this.delegate.matches.len().saturating_sub(1));
158 })
159 .log_err();
160 })
161 }
162
163 fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<BaseKeymapSelectorDelegate>>) {
164 if let Some(selection) = self.matches.get(self.selected_index) {
165 let base_keymap = BaseKeymap::from_names(&selection.string);
166
167 telemetry::event!(
168 "Settings Changed",
169 setting = "keymap",
170 value = base_keymap.to_string()
171 );
172
173 update_settings_file::<BaseKeymap>(self.fs.clone(), cx, move |setting, _| {
174 *setting = Some(base_keymap)
175 });
176 }
177
178 self.view
179 .update(cx, |_, cx| {
180 cx.emit(DismissEvent);
181 })
182 .ok();
183 }
184
185 fn dismissed(&mut self, cx: &mut ViewContext<Picker<BaseKeymapSelectorDelegate>>) {
186 self.view
187 .update(cx, |_, cx| {
188 cx.emit(DismissEvent);
189 })
190 .log_err();
191 }
192
193 fn render_match(
194 &self,
195 ix: usize,
196 selected: bool,
197 _cx: &mut ViewContext<Picker<Self>>,
198 ) -> Option<Self::ListItem> {
199 let keymap_match = &self.matches[ix];
200
201 Some(
202 ListItem::new(ix)
203 .inset(true)
204 .spacing(ListItemSpacing::Sparse)
205 .toggle_state(selected)
206 .child(HighlightedLabel::new(
207 keymap_match.string.clone(),
208 keymap_match.positions.clone(),
209 )),
210 )
211 }
212}