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