Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T05:06:21.369Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

settings_profile_selector.rs

732 lines · 24.7 KB · rust
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
Served at tenant.openagents/omega Member data and write actions are omitted.