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