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