1pub mod save_or_reopen {
2 use gpui::Styled;
3 use gpui::{AppContext, ParentElement};
4 use picker::Picker;
5 use picker::PickerDelegate;
6 use std::cell::RefCell;
7 use std::ops::{Deref, DerefMut};
8 use std::rc::Rc;
9 use std::sync::Arc;
10 use std::sync::atomic::AtomicBool;
11 use util::ResultExt;
12
13 use fuzzy::{StringMatch, StringMatchCandidate};
14 use gpui::{DismissEvent, Entity, EventEmitter, Focusable, WeakEntity};
15
16 use ui::{Context, HighlightedLabel, Label, ListItem, Render, Window, rems, v_flex};
17 use workspace::{ModalView, Workspace};
18
19 use crate::selectors::encoding::{Action, EncodingSelector, EncodingSelectorDelegate};
20
21 pub struct EncodingSaveOrReopenSelector {
22 picker: Entity<Picker<EncodingSaveOrReopenDelegate>>,
23 pub current_selection: usize,
24 workspace: WeakEntity<Workspace>,
25 }
26
27 impl EncodingSaveOrReopenSelector {
28 pub fn new(
29 window: &mut Window,
30 cx: &mut Context<EncodingSaveOrReopenSelector>,
31 workspace: WeakEntity<Workspace>,
32 ) -> Self {
33 let delegate =
34 EncodingSaveOrReopenDelegate::new(cx.entity().downgrade(), workspace.clone());
35
36 let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
37
38 Self {
39 picker,
40 current_selection: 0,
41 workspace,
42 }
43 }
44
45 pub fn toggle(workspace: &mut Workspace, window: &mut Window, cx: &mut Context<Workspace>) {
46 let weak_workspace = workspace.weak_handle();
47 workspace.toggle_modal(window, cx, |window, cx| {
48 EncodingSaveOrReopenSelector::new(window, cx, weak_workspace)
49 });
50 }
51 }
52
53 impl Focusable for EncodingSaveOrReopenSelector {
54 fn focus_handle(&self, cx: &ui::App) -> gpui::FocusHandle {
55 self.picker.focus_handle(cx)
56 }
57 }
58
59 impl Render for EncodingSaveOrReopenSelector {
60 fn render(
61 &mut self,
62 _window: &mut Window,
63 _cx: &mut Context<Self>,
64 ) -> impl ui::IntoElement {
65 v_flex().w(rems(34.0)).child(self.picker.clone())
66 }
67 }
68
69 impl ModalView for EncodingSaveOrReopenSelector {}
70
71 impl EventEmitter<DismissEvent> for EncodingSaveOrReopenSelector {}
72
73 pub struct EncodingSaveOrReopenDelegate {
74 selector: WeakEntity<EncodingSaveOrReopenSelector>,
75 current_selection: usize,
76 matches: Vec<StringMatch>,
77 pub actions: Vec<StringMatchCandidate>,
78 workspace: WeakEntity<Workspace>,
79 }
80
81 impl EncodingSaveOrReopenDelegate {
82 pub fn new(
83 selector: WeakEntity<EncodingSaveOrReopenSelector>,
84 workspace: WeakEntity<Workspace>,
85 ) -> Self {
86 Self {
87 selector,
88 current_selection: 0,
89 matches: Vec::new(),
90 actions: vec![
91 StringMatchCandidate::new(0, "Save with encoding"),
92 StringMatchCandidate::new(1, "Reopen with encoding"),
93 ],
94 workspace,
95 }
96 }
97
98 pub fn get_actions(&self) -> (&str, &str) {
99 (&self.actions[0].string, &self.actions[1].string)
100 }
101
102 pub fn post_selection(
103 &self,
104 cx: &mut Context<Picker<EncodingSaveOrReopenDelegate>>,
105 window: &mut Window,
106 ) {
107 if self.current_selection == 0 {
108 if let Some(workspace) = self.workspace.upgrade() {
109 workspace.update(cx, |workspace, cx| {
110 workspace.toggle_modal(window, cx, |window, cx| {
111 EncodingSelector::new(window, cx, Action::Save)
112 })
113 });
114 }
115 } else if self.current_selection == 1 {
116 if let Some(workspace) = self.workspace.upgrade() {
117 workspace.update(cx, |workspace, cx| {
118 workspace.toggle_modal(window, cx, |window, cx| {
119 EncodingSelector::new(window, cx, Action::Reopen)
120 })
121 });
122 }
123 }
124 }
125 }
126
127 impl PickerDelegate for EncodingSaveOrReopenDelegate {
128 type ListItem = ListItem;
129
130 fn match_count(&self) -> usize {
131 self.matches.len()
132 }
133
134 fn selected_index(&self) -> usize {
135 self.current_selection
136 }
137
138 fn set_selected_index(
139 &mut self,
140 ix: usize,
141 _window: &mut Window,
142 cx: &mut Context<Picker<Self>>,
143 ) {
144 self.current_selection = ix;
145 self.selector
146 .update(cx, |selector, cx| {
147 selector.current_selection = ix;
148 })
149 .log_err();
150 }
151
152 fn placeholder_text(&self, _window: &mut Window, _cx: &mut ui::App) -> std::sync::Arc<str> {
153 "Select an action...".into()
154 }
155
156 fn update_matches(
157 &mut self,
158 query: String,
159 window: &mut Window,
160 cx: &mut Context<Picker<Self>>,
161 ) -> gpui::Task<()> {
162 let executor = cx.background_executor().clone();
163 let actions = self.actions.clone();
164
165 cx.spawn_in(window, async move |this, cx| {
166 let matches = if query.is_empty() {
167 actions
168 .into_iter()
169 .enumerate()
170 .map(|(index, value)| StringMatch {
171 candidate_id: index,
172 score: 0.0,
173 positions: vec![],
174 string: value.string,
175 })
176 .collect::<Vec<StringMatch>>()
177 } else {
178 fuzzy::match_strings(
179 &actions,
180 &query,
181 false,
182 false,
183 2,
184 &AtomicBool::new(false),
185 executor,
186 )
187 .await
188 };
189
190 this.update(cx, |picker, cx| {
191 let delegate = &mut picker.delegate;
192 delegate.matches = matches;
193 delegate.current_selection = delegate
194 .current_selection
195 .min(delegate.matches.len().saturating_sub(1));
196 delegate
197 .selector
198 .update(cx, |selector, cx| {
199 selector.current_selection = delegate.current_selection
200 })
201 .log_err();
202 cx.notify();
203 })
204 .log_err();
205 })
206 }
207
208 fn confirm(&mut self, _: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
209 self.dismissed(window, cx);
210 if self.selector.is_upgradable() {
211 self.post_selection(cx, window);
212 }
213 }
214
215 fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
216 self.selector
217 .update(cx, |_, cx| cx.emit(DismissEvent))
218 .log_err();
219 }
220
221 fn render_match(
222 &self,
223 ix: usize,
224 _: bool,
225 _: &mut Window,
226 _: &mut Context<Picker<Self>>,
227 ) -> Option<Self::ListItem> {
228 Some(
229 ListItem::new(ix)
230 .child(HighlightedLabel::new(
231 &self.matches[ix].string,
232 self.matches[ix].positions.clone(),
233 ))
234 .spacing(ui::ListItemSpacing::Sparse),
235 )
236 }
237 }
238
239 pub fn get_current_encoding() -> &'static str {
240 "UTF-8"
241 }
242}
243
244pub mod encoding {
245 use std::{
246 ops::DerefMut,
247 rc::{Rc, Weak},
248 sync::{Arc, atomic::AtomicBool},
249 };
250
251 use fuzzy::{StringMatch, StringMatchCandidate};
252 use gpui::{
253 AppContext, BackgroundExecutor, DismissEvent, Entity, EventEmitter, Focusable, Length,
254 WeakEntity, actions,
255 };
256 use picker::{Picker, PickerDelegate};
257 use ui::{
258 Context, DefiniteLength, HighlightedLabel, Label, ListItem, ListItemSpacing, ParentElement,
259 Render, Styled, Window, rems, v_flex,
260 };
261 use util::{ResultExt, TryFutureExt};
262 use workspace::{ModalView, Workspace};
263
264 pub struct EncodingSelector {
265 picker: Entity<Picker<EncodingSelectorDelegate>>,
266 action: Action,
267 }
268
269 pub struct EncodingSelectorDelegate {
270 current_selection: usize,
271 encodings: Vec<StringMatchCandidate>,
272 matches: Vec<StringMatch>,
273 selector: WeakEntity<EncodingSelector>,
274 }
275
276 impl EncodingSelectorDelegate {
277 pub fn new(selector: WeakEntity<EncodingSelector>) -> EncodingSelectorDelegate {
278 EncodingSelectorDelegate {
279 current_selection: 0,
280 encodings: vec![
281 StringMatchCandidate::new(0, "UTF-8"),
282 StringMatchCandidate::new(1, "UTF-16 LE"),
283 StringMatchCandidate::new(2, "UTF-16 BE"),
284 StringMatchCandidate::new(3, "IBM866"),
285 StringMatchCandidate::new(4, "ISO 8859-1"),
286 StringMatchCandidate::new(5, "ISO 8859-2"),
287 StringMatchCandidate::new(6, "ISO 8859-3"),
288 StringMatchCandidate::new(7, "ISO 8859-4"),
289 StringMatchCandidate::new(8, "ISO 8859-5"),
290 StringMatchCandidate::new(9, "ISO 8859-6"),
291 StringMatchCandidate::new(10, "ISO 8859-7"),
292 StringMatchCandidate::new(11, "ISO 8859-8"),
293 StringMatchCandidate::new(12, "ISO 8859-10"),
294 StringMatchCandidate::new(13, "ISO 8859-13"),
295 StringMatchCandidate::new(14, "ISO 8859-14"),
296 StringMatchCandidate::new(15, "ISO 8859-15"),
297 StringMatchCandidate::new(16, "ISO 8859-16"),
298 StringMatchCandidate::new(17, "KOI8-R"),
299 StringMatchCandidate::new(18, "KOI8-U"),
300 StringMatchCandidate::new(19, "MacRoman"),
301 StringMatchCandidate::new(20, "Mac Cyrillic"),
302 StringMatchCandidate::new(21, "Windows-874"),
303 StringMatchCandidate::new(22, "Windows-1250"),
304 StringMatchCandidate::new(23, "Windows-1251"),
305 StringMatchCandidate::new(24, "Windows-1252"),
306 StringMatchCandidate::new(25, "Windows-1253"),
307 StringMatchCandidate::new(26, "Windows-1254"),
308 StringMatchCandidate::new(27, "Windows-1255"),
309 StringMatchCandidate::new(28, "Windows-1256"),
310 StringMatchCandidate::new(29, "Windows-1257"),
311 StringMatchCandidate::new(30, "Windows-1258"),
312 StringMatchCandidate::new(31, "EUC-KR"),
313 StringMatchCandidate::new(32, "EUC-JP"),
314 StringMatchCandidate::new(33, "Shift_JIS"),
315 StringMatchCandidate::new(34, "ISO 2022-JP"),
316 StringMatchCandidate::new(35, "GBK"),
317 StringMatchCandidate::new(36, "GB18030"),
318 StringMatchCandidate::new(37, "Big5"),
319 StringMatchCandidate::new(38, "HZ-GB-2312"),
320 ],
321 matches: Vec::new(),
322 selector,
323 }
324 }
325 }
326
327 impl PickerDelegate for EncodingSelectorDelegate {
328 type ListItem = ListItem;
329
330 fn match_count(&self) -> usize {
331 self.matches.len()
332 }
333
334 fn selected_index(&self) -> usize {
335 self.current_selection
336 }
337
338 fn set_selected_index(
339 &mut self,
340 ix: usize,
341 window: &mut Window,
342 cx: &mut Context<Picker<Self>>,
343 ) {
344 self.current_selection = ix;
345 }
346
347 fn placeholder_text(&self, _window: &mut Window, _cx: &mut ui::App) -> std::sync::Arc<str> {
348 "Select an encoding...".into()
349 }
350
351 fn update_matches(
352 &mut self,
353 query: String,
354 window: &mut Window,
355 cx: &mut Context<Picker<Self>>,
356 ) -> gpui::Task<()> {
357 let executor = cx.background_executor().clone();
358 let encodings = self.encodings.clone();
359
360 cx.spawn_in(window, async move |picker, cx| {
361 let matches: Vec<StringMatch>;
362
363 if query.is_empty() {
364 matches = encodings
365 .into_iter()
366 .enumerate()
367 .map(|(index, value)| StringMatch {
368 candidate_id: index,
369 score: 0.0,
370 positions: Vec::new(),
371 string: value.string,
372 })
373 .collect();
374 } else {
375 matches = fuzzy::match_strings(
376 &encodings,
377 &query,
378 true,
379 false,
380 38,
381 &AtomicBool::new(false),
382 executor,
383 )
384 .await
385 }
386
387 picker
388 .update(cx, |picker, cx| {
389 let delegate = &mut picker.delegate;
390 delegate.matches = matches;
391 delegate.current_selection = delegate
392 .current_selection
393 .min(delegate.matches.len().saturating_sub(1));
394 cx.notify();
395 })
396 .log_err();
397 })
398 }
399
400 fn confirm(
401 &mut self,
402 secondary: bool,
403 window: &mut Window,
404 cx: &mut Context<Picker<Self>>,
405 ) {
406 }
407
408 fn dismissed(&mut self, window: &mut Window, cx: &mut Context<Picker<Self>>) {
409 self.selector
410 .update(cx, |_, cx| cx.emit(DismissEvent))
411 .log_err();
412 }
413
414 fn render_match(
415 &self,
416 ix: usize,
417 selected: bool,
418 window: &mut Window,
419 cx: &mut Context<Picker<Self>>,
420 ) -> Option<Self::ListItem> {
421 Some(
422 ListItem::new(ix)
423 .child(HighlightedLabel::new(
424 &self.matches[ix].string,
425 self.matches[ix].positions.clone(),
426 ))
427 .spacing(ListItemSpacing::Sparse),
428 )
429 }
430 }
431
432 pub enum Action {
433 Save,
434 Reopen,
435 }
436
437 impl EncodingSelector {
438 pub fn new(
439 window: &mut Window,
440 cx: &mut Context<EncodingSelector>,
441 action: Action,
442 ) -> EncodingSelector {
443 let delegate = EncodingSelectorDelegate::new(cx.entity().downgrade());
444 let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
445
446 EncodingSelector { picker, action }
447 }
448 }
449
450 impl EventEmitter<DismissEvent> for EncodingSelector {}
451
452 impl Focusable for EncodingSelector {
453 fn focus_handle(&self, cx: &ui::App) -> gpui::FocusHandle {
454 cx.focus_handle()
455 }
456 }
457
458 impl ModalView for EncodingSelector {}
459
460 impl Render for EncodingSelector {
461 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl ui::IntoElement {
462 v_flex().w(rems(34.0)).child(self.picker.clone())
463 }
464 }
465}