Skip to repository content732 lines · 24.7 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T04:06:25.054Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
settings_profile_selector.rs
1use fuzzy::{StringMatch, StringMatchCandidate, match_strings};
2use gpui::{
3 App, Context, DismissEvent, Entity, EventEmitter, Focusable, Render, Task, WeakEntity, Window,
4};
5use picker::{Picker, PickerDelegate};
6use settings::{ActiveSettingsProfileName, SettingsStore};
7use ui::{HighlightedLabel, ListItem, ListItemSpacing, prelude::*};
8use workspace::{ModalView, Workspace};
9
10pub fn init(cx: &mut App) {
11 cx.on_action(|_: &zed_actions::settings_profile_selector::Toggle, cx| {
12 workspace::with_active_or_new_workspace(cx, |workspace, window, cx| {
13 toggle_settings_profile_selector(workspace, window, cx);
14 });
15 });
16}
17
18fn toggle_settings_profile_selector(
19 workspace: &mut Workspace,
20 window: &mut Window,
21 cx: &mut Context<Workspace>,
22) {
23 workspace.toggle_modal(window, cx, |window, cx| {
24 let delegate = SettingsProfileSelectorDelegate::new(cx.entity().downgrade(), window, cx);
25 SettingsProfileSelector::new(delegate, window, cx)
26 });
27}
28
29pub struct SettingsProfileSelector {
30 picker: Entity<Picker<SettingsProfileSelectorDelegate>>,
31}
32
33impl ModalView for SettingsProfileSelector {}
34
35impl EventEmitter<DismissEvent> for SettingsProfileSelector {}
36
37impl Focusable for SettingsProfileSelector {
38 fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
39 self.picker.focus_handle(cx)
40 }
41}
42
43impl Render for SettingsProfileSelector {
44 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
45 v_flex().child(self.picker.clone())
46 }
47}
48
49impl SettingsProfileSelector {
50 pub fn new(
51 delegate: SettingsProfileSelectorDelegate,
52 window: &mut Window,
53 cx: &mut Context<Self>,
54 ) -> Self {
55 let picker =
56 cx.new(|cx| Picker::uniform_list(delegate, window, cx).initial_width(rems(22.)));
57 Self { picker }
58 }
59}
60
61pub struct SettingsProfileSelectorDelegate {
62 matches: Vec<StringMatch>,
63 profile_names: Vec<Option<String>>,
64 original_profile_name: Option<String>,
65 selected_profile_name: Option<String>,
66 selected_index: usize,
67 selection_completed: bool,
68 selector: WeakEntity<SettingsProfileSelector>,
69}
70
71impl SettingsProfileSelectorDelegate {
72 fn new(
73 selector: WeakEntity<SettingsProfileSelector>,
74 _: &mut Window,
75 cx: &mut Context<SettingsProfileSelector>,
76 ) -> Self {
77 let settings_store = cx.global::<SettingsStore>();
78 let mut profile_names: Vec<Option<String>> = settings_store
79 .configured_settings_profiles()
80 .map(|s| Some(s.to_string()))
81 .collect();
82 profile_names.insert(0, None);
83
84 let matches = profile_names
85 .iter()
86 .enumerate()
87 .map(|(ix, profile_name)| StringMatch {
88 candidate_id: ix,
89 score: 0.0,
90 positions: Default::default(),
91 string: display_name(profile_name),
92 })
93 .collect();
94
95 let profile_name = cx
96 .try_global::<ActiveSettingsProfileName>()
97 .map(|p| p.0.clone());
98
99 let mut this = Self {
100 matches,
101 profile_names,
102 original_profile_name: profile_name.clone(),
103 selected_profile_name: None,
104 selected_index: 0,
105 selection_completed: false,
106 selector,
107 };
108
109 if let Some(profile_name) = profile_name {
110 this.select_if_matching(&profile_name);
111 }
112
113 this
114 }
115
116 fn select_if_matching(&mut self, profile_name: &str) {
117 self.selected_index = self
118 .matches
119 .iter()
120 .position(|mat| mat.string == profile_name)
121 .unwrap_or(self.selected_index);
122 }
123
124 fn set_selected_profile(
125 &self,
126 cx: &mut Context<Picker<SettingsProfileSelectorDelegate>>,
127 ) -> Option<String> {
128 let mat = self.matches.get(self.selected_index)?;
129 let profile_name = self.profile_names.get(mat.candidate_id)?;
130 Self::update_active_profile_name_global(profile_name.clone(), cx)
131 }
132
133 fn update_active_profile_name_global(
134 profile_name: Option<String>,
135 cx: &mut Context<Picker<SettingsProfileSelectorDelegate>>,
136 ) -> Option<String> {
137 if let Some(profile_name) = profile_name {
138 cx.set_global(ActiveSettingsProfileName(profile_name.clone()));
139 return Some(profile_name);
140 }
141
142 if cx.has_global::<ActiveSettingsProfileName>() {
143 cx.remove_global::<ActiveSettingsProfileName>();
144 }
145
146 None
147 }
148}
149
150impl PickerDelegate for SettingsProfileSelectorDelegate {
151 type ListItem = ListItem;
152
153 fn name() -> &'static str {
154 "settings profile selector"
155 }
156
157 fn placeholder_text(&self, _: &mut Window, _: &mut App) -> std::sync::Arc<str> {
158 "Select a settings profile...".into()
159 }
160
161 fn match_count(&self) -> usize {
162 self.matches.len()
163 }
164
165 fn selected_index(&self) -> usize {
166 self.selected_index
167 }
168
169 fn set_selected_index(
170 &mut self,
171 ix: usize,
172 _: &mut Window,
173 cx: &mut Context<Picker<SettingsProfileSelectorDelegate>>,
174 ) {
175 self.selected_index = ix;
176 self.selected_profile_name = self.set_selected_profile(cx);
177 }
178
179 fn update_matches(
180 &mut self,
181 query: String,
182 window: &mut Window,
183 cx: &mut Context<Picker<SettingsProfileSelectorDelegate>>,
184 ) -> Task<()> {
185 let background = cx.background_executor().clone();
186 let candidates = self
187 .profile_names
188 .iter()
189 .enumerate()
190 .map(|(id, profile_name)| StringMatchCandidate::new(id, &display_name(profile_name)))
191 .collect::<Vec<_>>();
192
193 cx.spawn_in(window, async move |this, cx| {
194 let matches = if query.is_empty() {
195 candidates
196 .into_iter()
197 .enumerate()
198 .map(|(index, candidate)| StringMatch {
199 candidate_id: index,
200 string: candidate.string,
201 positions: Vec::new(),
202 score: 0.0,
203 })
204 .collect()
205 } else {
206 match_strings(
207 &candidates,
208 &query,
209 false,
210 true,
211 100,
212 &Default::default(),
213 background,
214 )
215 .await
216 };
217
218 this.update_in(cx, |this, _, cx| {
219 this.delegate.matches = matches;
220 this.delegate.selected_index = this
221 .delegate
222 .selected_index
223 .min(this.delegate.matches.len().saturating_sub(1));
224 this.delegate.selected_profile_name = this.delegate.set_selected_profile(cx);
225 })
226 .ok();
227 })
228 }
229
230 fn confirm(
231 &mut self,
232 _: bool,
233 _: &mut Window,
234 cx: &mut Context<Picker<SettingsProfileSelectorDelegate>>,
235 ) {
236 self.selection_completed = true;
237 self.selector
238 .update(cx, |_, cx| {
239 cx.emit(DismissEvent);
240 })
241 .ok();
242 }
243
244 fn dismissed(
245 &mut self,
246 _: &mut Window,
247 cx: &mut Context<Picker<SettingsProfileSelectorDelegate>>,
248 ) {
249 if !self.selection_completed {
250 SettingsProfileSelectorDelegate::update_active_profile_name_global(
251 self.original_profile_name.clone(),
252 cx,
253 );
254 }
255 self.selector.update(cx, |_, cx| cx.emit(DismissEvent)).ok();
256 }
257
258 fn render_match(
259 &self,
260 ix: usize,
261 selected: bool,
262 _: &mut Window,
263 _: &mut Context<Picker<Self>>,
264 ) -> Option<Self::ListItem> {
265 let mat = &self.matches.get(ix)?;
266 let profile_name = &self.profile_names.get(mat.candidate_id)?;
267
268 Some(
269 ListItem::new(ix)
270 .inset(true)
271 .spacing(ListItemSpacing::Sparse)
272 .toggle_state(selected)
273 .child(HighlightedLabel::new(
274 display_name(profile_name),
275 mat.positions.clone(),
276 )),
277 )
278 }
279}
280
281fn display_name(profile_name: &Option<String>) -> String {
282 profile_name.clone().unwrap_or("Disabled".into())
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288 use editor;
289 use gpui::{TestAppContext, UpdateGlobal, VisualTestContext};
290 use menu::{Cancel, Confirm, SelectNext, SelectPrevious};
291 use project::{FakeFs, Project};
292 use serde_json::json;
293 use settings::Settings;
294 use theme_settings::ThemeSettings;
295 use workspace::{self, AppState, MultiWorkspace};
296 use zed_actions::settings_profile_selector;
297
298 async fn init_test(
299 user_settings_json: serde_json::Value,
300 cx: &mut TestAppContext,
301 ) -> (Entity<Workspace>, &mut VisualTestContext) {
302 cx.update(|cx| {
303 let state = AppState::test(cx);
304 let settings_store = SettingsStore::test(cx);
305 cx.set_global(settings_store);
306 settings::init(cx);
307 theme_settings::init(theme::LoadThemes::JustBase, cx);
308 super::init(cx);
309 editor::init(cx);
310 state
311 });
312
313 cx.update(|cx| {
314 SettingsStore::update_global(cx, |store, cx| {
315 store
316 .set_user_settings(&user_settings_json.to_string(), cx)
317 .unwrap();
318 });
319 });
320
321 let fs = FakeFs::new(cx.executor());
322 let project = Project::test(fs, ["/test".as_ref()], cx).await;
323 let window = cx.add_window(|window, cx| MultiWorkspace::test_new(project, window, cx));
324 let cx = VisualTestContext::from_window(*window, cx).into_mut();
325 let workspace = window
326 .read_with(cx, |mw, _| mw.workspace().clone())
327 .unwrap();
328
329 cx.update(|_, cx| {
330 assert!(!cx.has_global::<ActiveSettingsProfileName>());
331 });
332
333 (workspace, cx)
334 }
335
336 #[track_caller]
337 fn active_settings_profile_picker(
338 workspace: &Entity<Workspace>,
339 cx: &mut VisualTestContext,
340 ) -> Entity<Picker<SettingsProfileSelectorDelegate>> {
341 workspace.update(cx, |workspace, cx| {
342 workspace
343 .active_modal::<SettingsProfileSelector>(cx)
344 .expect("settings profile selector is not open")
345 .read(cx)
346 .picker
347 .clone()
348 })
349 }
350
351 #[gpui::test]
352 async fn test_settings_profile_selector_state(cx: &mut TestAppContext) {
353 let classroom_and_streaming_profile_name = "Classroom / Streaming".to_string();
354 let demo_videos_profile_name = "Demo Videos".to_string();
355
356 let user_settings_json = json!({
357 "buffer_font_size": 10.0,
358 "profiles": {
359 classroom_and_streaming_profile_name.clone(): {
360 "settings": {
361 "buffer_font_size": 20.0,
362 }
363 },
364 demo_videos_profile_name.clone(): {
365 "settings": {
366 "buffer_font_size": 15.0
367 }
368 }
369 }
370 });
371 let (workspace, cx) = init_test(user_settings_json, cx).await;
372
373 cx.dispatch_action(settings_profile_selector::Toggle);
374 let picker = active_settings_profile_picker(&workspace, cx);
375
376 picker.read_with(cx, |picker, cx| {
377 assert_eq!(picker.delegate.matches.len(), 3);
378 assert_eq!(picker.delegate.matches[0].string, display_name(&None));
379 assert_eq!(
380 picker.delegate.matches[1].string,
381 classroom_and_streaming_profile_name
382 );
383 assert_eq!(picker.delegate.matches[2].string, demo_videos_profile_name);
384 assert_eq!(picker.delegate.matches.get(3), None);
385
386 assert_eq!(picker.delegate.selected_index, 0);
387 assert_eq!(picker.delegate.selected_profile_name, None);
388
389 assert_eq!(cx.try_global::<ActiveSettingsProfileName>(), None);
390 assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(10.0));
391 });
392
393 cx.dispatch_action(Confirm);
394
395 cx.update(|_, cx| {
396 assert_eq!(cx.try_global::<ActiveSettingsProfileName>(), None);
397 });
398
399 cx.dispatch_action(settings_profile_selector::Toggle);
400 let picker = active_settings_profile_picker(&workspace, cx);
401 cx.dispatch_action(SelectNext);
402
403 picker.read_with(cx, |picker, cx| {
404 assert_eq!(picker.delegate.selected_index, 1);
405 assert_eq!(
406 picker.delegate.selected_profile_name,
407 Some(classroom_and_streaming_profile_name.clone())
408 );
409
410 assert_eq!(
411 cx.try_global::<ActiveSettingsProfileName>()
412 .map(|p| p.0.clone()),
413 Some(classroom_and_streaming_profile_name.clone())
414 );
415
416 assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(20.0));
417 });
418
419 cx.dispatch_action(Cancel);
420
421 cx.update(|_, cx| {
422 assert_eq!(cx.try_global::<ActiveSettingsProfileName>(), None);
423 assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(10.0));
424 });
425
426 cx.dispatch_action(settings_profile_selector::Toggle);
427 let picker = active_settings_profile_picker(&workspace, cx);
428
429 cx.dispatch_action(SelectNext);
430
431 picker.read_with(cx, |picker, cx| {
432 assert_eq!(picker.delegate.selected_index, 1);
433 assert_eq!(
434 picker.delegate.selected_profile_name,
435 Some(classroom_and_streaming_profile_name.clone())
436 );
437
438 assert_eq!(
439 cx.try_global::<ActiveSettingsProfileName>()
440 .map(|p| p.0.clone()),
441 Some(classroom_and_streaming_profile_name.clone())
442 );
443
444 assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(20.0));
445 });
446
447 cx.dispatch_action(SelectNext);
448
449 picker.read_with(cx, |picker, cx| {
450 assert_eq!(picker.delegate.selected_index, 2);
451 assert_eq!(
452 picker.delegate.selected_profile_name,
453 Some(demo_videos_profile_name.clone())
454 );
455
456 assert_eq!(
457 cx.try_global::<ActiveSettingsProfileName>()
458 .map(|p| p.0.clone()),
459 Some(demo_videos_profile_name.clone())
460 );
461
462 assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(15.0));
463 });
464
465 cx.dispatch_action(Confirm);
466
467 cx.update(|_, cx| {
468 assert_eq!(
469 cx.try_global::<ActiveSettingsProfileName>()
470 .map(|p| p.0.clone()),
471 Some(demo_videos_profile_name.clone())
472 );
473 assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(15.0));
474 });
475
476 cx.dispatch_action(settings_profile_selector::Toggle);
477 let picker = active_settings_profile_picker(&workspace, cx);
478
479 picker.read_with(cx, |picker, cx| {
480 assert_eq!(picker.delegate.selected_index, 2);
481 assert_eq!(
482 picker.delegate.selected_profile_name,
483 Some(demo_videos_profile_name.clone())
484 );
485
486 assert_eq!(
487 cx.try_global::<ActiveSettingsProfileName>()
488 .map(|p| p.0.clone()),
489 Some(demo_videos_profile_name.clone())
490 );
491 assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(15.0));
492 });
493
494 cx.dispatch_action(SelectPrevious);
495
496 picker.read_with(cx, |picker, cx| {
497 assert_eq!(picker.delegate.selected_index, 1);
498 assert_eq!(
499 picker.delegate.selected_profile_name,
500 Some(classroom_and_streaming_profile_name.clone())
501 );
502
503 assert_eq!(
504 cx.try_global::<ActiveSettingsProfileName>()
505 .map(|p| p.0.clone()),
506 Some(classroom_and_streaming_profile_name.clone())
507 );
508
509 assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(20.0));
510 });
511
512 cx.dispatch_action(Cancel);
513
514 cx.update(|_, cx| {
515 assert_eq!(
516 cx.try_global::<ActiveSettingsProfileName>()
517 .map(|p| p.0.clone()),
518 Some(demo_videos_profile_name.clone())
519 );
520
521 assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(15.0));
522 });
523
524 cx.dispatch_action(settings_profile_selector::Toggle);
525 let picker = active_settings_profile_picker(&workspace, cx);
526
527 picker.read_with(cx, |picker, cx| {
528 assert_eq!(picker.delegate.selected_index, 2);
529 assert_eq!(
530 picker.delegate.selected_profile_name,
531 Some(demo_videos_profile_name.clone())
532 );
533
534 assert_eq!(
535 cx.try_global::<ActiveSettingsProfileName>()
536 .map(|p| p.0.clone()),
537 Some(demo_videos_profile_name)
538 );
539
540 assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(15.0));
541 });
542
543 cx.dispatch_action(SelectPrevious);
544
545 picker.read_with(cx, |picker, cx| {
546 assert_eq!(picker.delegate.selected_index, 1);
547 assert_eq!(
548 picker.delegate.selected_profile_name,
549 Some(classroom_and_streaming_profile_name.clone())
550 );
551
552 assert_eq!(
553 cx.try_global::<ActiveSettingsProfileName>()
554 .map(|p| p.0.clone()),
555 Some(classroom_and_streaming_profile_name)
556 );
557
558 assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(20.0));
559 });
560
561 cx.dispatch_action(SelectPrevious);
562
563 picker.read_with(cx, |picker, cx| {
564 assert_eq!(picker.delegate.selected_index, 0);
565 assert_eq!(picker.delegate.selected_profile_name, None);
566
567 assert_eq!(
568 cx.try_global::<ActiveSettingsProfileName>()
569 .map(|p| p.0.clone()),
570 None
571 );
572
573 assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(10.0));
574 });
575
576 cx.dispatch_action(Confirm);
577
578 cx.update(|_, cx| {
579 assert_eq!(cx.try_global::<ActiveSettingsProfileName>(), None);
580 assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(10.0));
581 });
582 }
583
584 #[gpui::test]
585 async fn test_settings_profile_with_user_base(cx: &mut TestAppContext) {
586 let user_settings_json = json!({
587 "buffer_font_size": 10.0,
588 "profiles": {
589 "Explicit User": {
590 "base": "user",
591 "settings": {
592 "buffer_font_size": 20.0
593 }
594 },
595 "Implicit User": {
596 "settings": {
597 "buffer_font_size": 20.0
598 }
599 }
600 }
601 });
602 let (workspace, cx) = init_test(user_settings_json, cx).await;
603
604 // Select "Explicit User" (index 1) — profile applies on top of user settings.
605 cx.dispatch_action(settings_profile_selector::Toggle);
606 let picker = active_settings_profile_picker(&workspace, cx);
607 cx.dispatch_action(SelectNext);
608
609 picker.read_with(cx, |picker, cx| {
610 assert_eq!(
611 picker.delegate.selected_profile_name.as_deref(),
612 Some("Explicit User")
613 );
614 assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(20.0));
615 });
616
617 cx.dispatch_action(Confirm);
618
619 // Select "Implicit User" (index 2) — no base specified, same behavior.
620 cx.dispatch_action(settings_profile_selector::Toggle);
621 let picker = active_settings_profile_picker(&workspace, cx);
622 cx.dispatch_action(SelectNext);
623
624 picker.read_with(cx, |picker, cx| {
625 assert_eq!(
626 picker.delegate.selected_profile_name.as_deref(),
627 Some("Implicit User")
628 );
629 assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(20.0));
630 });
631
632 cx.dispatch_action(Confirm);
633 }
634
635 #[gpui::test]
636 async fn test_settings_profile_with_default_base(cx: &mut TestAppContext) {
637 let user_settings_json = json!({
638 "buffer_font_size": 10.0,
639 "profiles": {
640 "Clean Slate": {
641 "base": "default"
642 },
643 "Custom on Defaults": {
644 "base": "default",
645 "settings": {
646 "buffer_font_size": 30.0
647 }
648 }
649 }
650 });
651 let (workspace, cx) = init_test(user_settings_json, cx).await;
652
653 // User has buffer_font_size: 10, factory default is 15.
654 cx.update(|_, cx| {
655 assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(10.0));
656 });
657
658 // "Clean Slate" has base: "default" with no settings overrides,
659 // so we get the factory default (15), not the user's value (10).
660 cx.dispatch_action(settings_profile_selector::Toggle);
661 let picker = active_settings_profile_picker(&workspace, cx);
662 cx.dispatch_action(SelectNext);
663
664 picker.read_with(cx, |picker, cx| {
665 assert_eq!(
666 picker.delegate.selected_profile_name.as_deref(),
667 Some("Clean Slate")
668 );
669 assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(15.0));
670 });
671
672 // "Custom on Defaults" has base: "default" with buffer_font_size: 30,
673 // so the profile's override (30) applies on top of the factory default,
674 // not on top of the user's value (10).
675 cx.dispatch_action(SelectNext);
676
677 picker.read_with(cx, |picker, cx| {
678 assert_eq!(
679 picker.delegate.selected_profile_name.as_deref(),
680 Some("Custom on Defaults")
681 );
682 assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(30.0));
683 });
684
685 cx.dispatch_action(Confirm);
686
687 cx.update(|_, cx| {
688 assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(30.0));
689 });
690 }
691
692 #[gpui::test]
693 async fn test_settings_profile_selector_is_in_user_configuration_order(
694 cx: &mut TestAppContext,
695 ) {
696 // Must be unique names (HashMap)
697 let user_settings_json = json!({
698 "profiles": {
699 "z": { "settings": {} },
700 "e": { "settings": {} },
701 "d": { "settings": {} },
702 " ": { "settings": {} },
703 "r": { "settings": {} },
704 "u": { "settings": {} },
705 "l": { "settings": {} },
706 "3": { "settings": {} },
707 "s": { "settings": {} },
708 "!": { "settings": {} },
709 }
710 });
711 let (workspace, cx) = init_test(user_settings_json, cx).await;
712
713 cx.dispatch_action(settings_profile_selector::Toggle);
714 let picker = active_settings_profile_picker(&workspace, cx);
715
716 picker.read_with(cx, |picker, _| {
717 assert_eq!(picker.delegate.matches.len(), 11);
718 assert_eq!(picker.delegate.matches[0].string, display_name(&None));
719 assert_eq!(picker.delegate.matches[1].string, "z");
720 assert_eq!(picker.delegate.matches[2].string, "e");
721 assert_eq!(picker.delegate.matches[3].string, "d");
722 assert_eq!(picker.delegate.matches[4].string, " ");
723 assert_eq!(picker.delegate.matches[5].string, "r");
724 assert_eq!(picker.delegate.matches[6].string, "u");
725 assert_eq!(picker.delegate.matches[7].string, "l");
726 assert_eq!(picker.delegate.matches[8].string, "3");
727 assert_eq!(picker.delegate.matches[9].string, "s");
728 assert_eq!(picker.delegate.matches[10].string, "!");
729 });
730 }
731}
732