Skip to repository content10659 lines · 485.9 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T05:37:50.656Z 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
page_data.rs
1use gpui::{Action as _, App};
2use itertools::Itertools as _;
3use settings::{
4 AudioInputDeviceName, AudioOutputDeviceName, EditPredictionDataCollectionChoice,
5 LanguageSettingsContent, SemanticTokens, SettingsContent,
6};
7use std::sync::{Arc, OnceLock};
8use strum::{EnumMessage, IntoDiscriminant as _, VariantArray};
9use theme::SystemAppearance;
10use ui::IntoElement;
11
12use crate::{
13 ActionLink, DynamicItem, PROJECT, SettingField, SettingItem, SettingsFieldMetadata,
14 SettingsPage, SettingsPageItem, SubPageLink, USER, active_language, all_language_names,
15 pages::{
16 open_audio_test_window, render_edit_prediction_setup_page, render_external_agents_page,
17 render_llm_providers_page, render_mcp_servers_page, render_sandbox_settings_page,
18 render_skills_setup_page, render_tool_permissions_setup_page,
19 },
20};
21
22const DEFAULT_STRING: String = String::new();
23/// A default empty string reference. Useful in `pick` functions for cases either in dynamic item fields, or when dealing with `settings::Maybe`
24/// to avoid the "NO DEFAULT" case.
25const DEFAULT_EMPTY_STRING: Option<&String> = Some(&DEFAULT_STRING);
26
27const DEFAULT_AUDIO_OUTPUT: AudioOutputDeviceName = AudioOutputDeviceName(None);
28const DEFAULT_EMPTY_AUDIO_OUTPUT: Option<&AudioOutputDeviceName> = Some(&DEFAULT_AUDIO_OUTPUT);
29const DEFAULT_AUDIO_INPUT: AudioInputDeviceName = AudioInputDeviceName(None);
30const DEFAULT_EMPTY_AUDIO_INPUT: Option<&AudioInputDeviceName> = Some(&DEFAULT_AUDIO_INPUT);
31
32macro_rules! concat_sections {
33 (@vec, $($arr:expr),+ $(,)?) => {{
34 let total_len = 0_usize $(+ $arr.len())+;
35 let mut out = Vec::with_capacity(total_len);
36
37 $(
38 out.extend($arr);
39 )+
40
41 out
42 }};
43
44 ($($arr:expr),+ $(,)?) => {{
45 let total_len = 0_usize $(+ $arr.len())+;
46
47 let mut out: Box<[std::mem::MaybeUninit<_>]> = Box::new_uninit_slice(total_len);
48
49 let mut index = 0usize;
50 $(
51 let array = $arr;
52 for item in array {
53 out[index].write(item);
54 index += 1;
55 }
56 )+
57
58 debug_assert_eq!(index, total_len);
59
60 // SAFETY: we wrote exactly `total_len` elements.
61 unsafe { out.assume_init() }
62 }};
63}
64
65pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
66 vec![
67 general_page(cx),
68 appearance_page(),
69 keymap_page(),
70 editor_page(),
71 languages_and_tools_page(cx),
72 search_and_files_page(),
73 window_and_layout_page(),
74 panels_page(),
75 debugger_page(),
76 terminal_page(),
77 version_control_page(),
78 collaboration_page(),
79 ai_page(cx),
80 network_page(),
81 developer_page(cx),
82 ]
83}
84
85fn developer_page(cx: &App) -> SettingsPage {
86 use feature_flags::FeatureFlagAppExt as _;
87
88 let mut items: Vec<SettingsPageItem> = Vec::new();
89
90 // Feature flag overrides are a staff-only affordance, so only surface the section when the overrides are enabled.
91 if cx.feature_flag_overrides_enabled() {
92 items.push(SettingsPageItem::SectionHeader("Feature Flags"));
93 items.push(SettingsPageItem::SubPageLink(SubPageLink {
94 title: "Feature Flags".into(),
95 r#type: Default::default(),
96 description: None,
97 search_aliases: &[],
98 json_path: Some("feature_flags"),
99 in_json: true,
100 files: USER,
101 render: crate::pages::render_feature_flags_page,
102 }));
103 }
104
105 items.push(SettingsPageItem::SectionHeader("Instrumentation"));
106 items.push(SettingsPageItem::SettingItem(SettingItem {
107 title: "Performance Profiler",
108 description: "Collect timing data for foreground and background executor tasks so they can be inspected via `omega: open performance profiler`. May lead to increased memory usage.",
109 field: Box::new(SettingField {
110 organization_override: None,
111 json_path: Some("instrumentation.performance_profiler.enabled"),
112 pick: |settings_content| {
113 settings_content
114 .instrumentation
115 .as_ref()
116 .and_then(|i| i.performance_profiler.as_ref())
117 .and_then(|p| p.enabled.as_ref())
118 },
119 write: |settings_content, value, _| {
120 settings_content
121 .instrumentation
122 .get_or_insert_default()
123 .performance_profiler
124 .get_or_insert_default()
125 .enabled = value;
126 },
127 }),
128 metadata: None,
129 files: USER,
130 }));
131
132 SettingsPage {
133 title: "Developer",
134 items: items.into_boxed_slice(),
135 }
136}
137
138fn general_page(cx: &App) -> SettingsPage {
139 fn general_settings_section(_cx: &App) -> Vec<SettingsPageItem> {
140 vec![
141 SettingsPageItem::SectionHeader("General Settings"),
142 SettingsPageItem::SettingItem(SettingItem {
143 title: "Accessible Mode",
144 description: "Optimize Omega's interface for assistive technology such as screen readers. When enabled, otherwise-collapsed controls stay expanded and keyboard-reachable.",
145 field: Box::new(SettingField {
146 organization_override: None,
147 json_path: Some("accessible_mode"),
148 pick: |settings_content| settings_content.workspace.accessible_mode.as_ref(),
149 write: |settings_content, value, _| {
150 settings_content.workspace.accessible_mode = value;
151 },
152 }),
153 metadata: None,
154 files: USER,
155 }),
156 SettingsPageItem::SettingItem(SettingItem {
157 title: "When Closing With No Tabs",
158 description: "What to do when using the 'close active item' action with no tabs.",
159 field: Box::new(SettingField {
160 organization_override: None,
161 json_path: Some("when_closing_with_no_tabs"),
162 pick: |settings_content| {
163 settings_content
164 .workspace
165 .when_closing_with_no_tabs
166 .as_ref()
167 },
168 write: |settings_content, value, _| {
169 settings_content.workspace.when_closing_with_no_tabs = value;
170 },
171 }),
172 metadata: None,
173 files: USER,
174 }),
175 SettingsPageItem::SettingItem(SettingItem {
176 title: "On Last Window Closed",
177 description: "What to do when the last window is closed.",
178 field: Box::new(SettingField {
179 organization_override: None,
180 json_path: Some("on_last_window_closed"),
181 pick: |settings_content| {
182 settings_content.workspace.on_last_window_closed.as_ref()
183 },
184 write: |settings_content, value, _| {
185 settings_content.workspace.on_last_window_closed = value;
186 },
187 }),
188 metadata: None,
189 files: USER,
190 }),
191 SettingsPageItem::SettingItem(SettingItem {
192 title: "Use System Path Prompts",
193 description: "Use native OS dialogs for 'Open' and 'Save As'.",
194 field: Box::new(SettingField {
195 organization_override: None,
196 json_path: Some("use_system_path_prompts"),
197 pick: |settings_content| {
198 settings_content.workspace.use_system_path_prompts.as_ref()
199 },
200 write: |settings_content, value, _| {
201 settings_content.workspace.use_system_path_prompts = value;
202 },
203 }),
204 metadata: None,
205 files: USER,
206 }),
207 SettingsPageItem::SettingItem(SettingItem {
208 title: "Use System Prompts",
209 description: "Use native OS dialogs for confirmations.",
210 field: Box::new(SettingField {
211 organization_override: None,
212 json_path: Some("use_system_prompts"),
213 pick: |settings_content| settings_content.workspace.use_system_prompts.as_ref(),
214 write: |settings_content, value, _| {
215 settings_content.workspace.use_system_prompts = value;
216 },
217 }),
218 metadata: None,
219 files: USER,
220 }),
221 SettingsPageItem::SettingItem(SettingItem {
222 title: "Redact Private Values",
223 description: "Hide the values of variables in private files.",
224 field: Box::new(SettingField {
225 organization_override: None,
226 json_path: Some("redact_private_values"),
227 pick: |settings_content| settings_content.editor.redact_private_values.as_ref(),
228 write: |settings_content, value, _| {
229 settings_content.editor.redact_private_values = value;
230 },
231 }),
232 metadata: None,
233 files: USER,
234 }),
235 SettingsPageItem::SettingItem(SettingItem {
236 title: "Private Files",
237 description: "Globs to match against file paths to determine if a file is private.",
238 field: Box::new(
239 SettingField {
240 organization_override: None,
241 json_path: Some("worktree.private_files"),
242 pick: |settings_content| {
243 settings_content.project.worktree.private_files.as_ref()
244 },
245 write: |settings_content, value, _| {
246 settings_content.project.worktree.private_files = value;
247 },
248 }
249 .unimplemented(),
250 ),
251 metadata: None,
252 files: USER,
253 }),
254 SettingsPageItem::SettingItem(SettingItem {
255 title: "CLI Default Open Behavior",
256 description: "How `omega <path>` opens directories when no flag is specified.",
257 field: Box::new(SettingField {
258 organization_override: None,
259 json_path: Some("cli_default_open_behavior"),
260 pick: |settings_content| {
261 settings_content
262 .workspace
263 .cli_default_open_behavior
264 .as_ref()
265 },
266 write: |settings_content, value, _| {
267 settings_content.workspace.cli_default_open_behavior = value;
268 },
269 }),
270 metadata: Some(Box::new(SettingsFieldMetadata {
271 should_do_titlecase: Some(false),
272 ..Default::default()
273 })),
274 files: USER,
275 }),
276 SettingsPageItem::SettingItem(SettingItem {
277 title: "Default Open Behavior",
278 description: "How projects open from the UI by default.",
279 field: Box::new(SettingField {
280 organization_override: None,
281 json_path: Some("default_open_behavior"),
282 pick: |settings_content| {
283 settings_content.workspace.default_open_behavior.as_ref()
284 },
285 write: |settings_content, value, _| {
286 settings_content.workspace.default_open_behavior = value;
287 },
288 }),
289 metadata: Some(Box::new(SettingsFieldMetadata {
290 should_do_titlecase: Some(false),
291 ..Default::default()
292 })),
293 files: USER,
294 }),
295 ]
296 }
297 fn security_section() -> [SettingsPageItem; 2] {
298 [
299 SettingsPageItem::SectionHeader("Security"),
300 SettingsPageItem::SettingItem(SettingItem {
301 title: "Trust All Projects By Default",
302 description: "Trust every project automatically. Omega defaults this on, so all features work in any project without per-project permission.",
303 field: Box::new(SettingField {
304 organization_override: None,
305 json_path: Some("session.trust_all_projects"),
306 pick: |settings_content| {
307 settings_content
308 .session
309 .as_ref()
310 .and_then(|session| session.trust_all_worktrees.as_ref())
311 },
312 write: |settings_content, value, _| {
313 settings_content
314 .session
315 .get_or_insert_default()
316 .trust_all_worktrees = value;
317 },
318 }),
319 metadata: None,
320 files: USER,
321 }),
322 ]
323 }
324
325 fn workspace_restoration_section() -> [SettingsPageItem; 3] {
326 [
327 SettingsPageItem::SectionHeader("Workspace Restoration"),
328 SettingsPageItem::SettingItem(SettingItem {
329 title: "Restore Unsaved Buffers",
330 description: "Whether or not to restore unsaved buffers on restart.",
331 field: Box::new(SettingField {
332 organization_override: None,
333 json_path: Some("session.restore_unsaved_buffers"),
334 pick: |settings_content| {
335 settings_content
336 .session
337 .as_ref()
338 .and_then(|session| session.restore_unsaved_buffers.as_ref())
339 },
340 write: |settings_content, value, _| {
341 settings_content
342 .session
343 .get_or_insert_default()
344 .restore_unsaved_buffers = value;
345 },
346 }),
347 metadata: None,
348 files: USER,
349 }),
350 SettingsPageItem::SettingItem(SettingItem {
351 title: "Restore On Startup",
352 description: "What to restore from the previous session when opening Omega.",
353 field: Box::new(SettingField {
354 organization_override: None,
355 json_path: Some("restore_on_startup"),
356 pick: |settings_content| settings_content.workspace.restore_on_startup.as_ref(),
357 write: |settings_content, value, _| {
358 settings_content.workspace.restore_on_startup = value;
359 },
360 }),
361 metadata: None,
362 files: USER,
363 }),
364 ]
365 }
366
367 fn scoped_settings_section() -> [SettingsPageItem; 3] {
368 [
369 SettingsPageItem::SectionHeader("Scoped Settings"),
370 SettingsPageItem::SettingItem(SettingItem {
371 files: USER,
372 title: "Preview Channel",
373 description: "Which settings should be activated only in Preview build of Omega.",
374 field: Box::new(
375 SettingField {
376 organization_override: None,
377 json_path: Some("preview_channel_settings"),
378 pick: |settings_content| Some(settings_content),
379 write: |_settings_content, _value, _| {},
380 }
381 .unimplemented(),
382 ),
383 metadata: None,
384 }),
385 SettingsPageItem::SettingItem(SettingItem {
386 files: USER,
387 title: "Settings Profiles",
388 description: "Any number of settings profiles that are temporarily applied on top of your existing user settings.",
389 field: Box::new(
390 SettingField {
391 organization_override: None,
392 json_path: Some("settings_profiles"),
393 pick: |settings_content| Some(settings_content),
394 write: |_settings_content, _value, _| {},
395 }
396 .unimplemented(),
397 ),
398 metadata: None,
399 }),
400 ]
401 }
402
403 fn privacy_section() -> [SettingsPageItem; 4] {
404 [
405 SettingsPageItem::SectionHeader("Privacy"),
406 SettingsPageItem::SettingItem(SettingItem {
407 title: "Telemetry Diagnostics",
408 description: "Send debug information like crash reports.",
409 field: Box::new(SettingField {
410 organization_override: None,
411 json_path: Some("telemetry.diagnostics"),
412 pick: |settings_content| {
413 settings_content
414 .telemetry
415 .as_ref()
416 .and_then(|telemetry| telemetry.diagnostics.as_ref())
417 },
418 write: |settings_content, value, _| {
419 settings_content
420 .telemetry
421 .get_or_insert_default()
422 .diagnostics = value;
423 },
424 }),
425 metadata: None,
426 files: USER,
427 }),
428 SettingsPageItem::SettingItem(SettingItem {
429 title: "Telemetry Metrics",
430 description: "Send anonymized usage data like what languages you're using Omega with.",
431 field: Box::new(SettingField {
432 organization_override: None,
433 json_path: Some("telemetry.metrics"),
434 pick: |settings_content| {
435 settings_content
436 .telemetry
437 .as_ref()
438 .and_then(|telemetry| telemetry.metrics.as_ref())
439 },
440 write: |settings_content, value, _| {
441 settings_content.telemetry.get_or_insert_default().metrics = value;
442 },
443 }),
444 metadata: None,
445 files: USER,
446 }),
447 SettingsPageItem::SettingItem(SettingItem {
448 title: "Anthropic Data Retention",
449 description: "Allow sending requests to Anthropic models that cannot be offered with Zero Data Retention.",
450 field: Box::new(SettingField {
451 organization_override: None,
452 json_path: Some("telemetry.anthropic_retention"),
453 pick: |settings_content| {
454 settings_content
455 .telemetry
456 .as_ref()
457 .and_then(|telemetry| telemetry.anthropic_retention.as_ref())
458 },
459 write: |settings_content, value, _| {
460 settings_content
461 .telemetry
462 .get_or_insert_default()
463 .anthropic_retention = value;
464 },
465 }),
466 metadata: None,
467 files: USER,
468 }),
469 ]
470 }
471
472 fn auto_update_section() -> [SettingsPageItem; 2] {
473 [
474 SettingsPageItem::SectionHeader("Auto Update"),
475 SettingsPageItem::SettingItem(SettingItem {
476 title: "Auto Update",
477 description: "Whether or not to automatically check for updates.",
478 field: Box::new(SettingField {
479 organization_override: None,
480 json_path: Some("auto_update"),
481 pick: |settings_content| settings_content.auto_update.as_ref(),
482 write: |settings_content, value, _| {
483 settings_content.auto_update = value;
484 },
485 }),
486 metadata: None,
487 files: USER,
488 }),
489 ]
490 }
491
492 SettingsPage {
493 title: "General",
494 items: concat_sections!(
495 @vec,
496 general_settings_section(cx),
497 security_section(),
498 workspace_restoration_section(),
499 scoped_settings_section(),
500 privacy_section(),
501 auto_update_section(),
502 )
503 .into(),
504 }
505}
506
507fn appearance_page() -> SettingsPage {
508 fn theme_section() -> [SettingsPageItem; 3] {
509 [
510 SettingsPageItem::SectionHeader("Theme"),
511 SettingsPageItem::DynamicItem(DynamicItem {
512 discriminant: SettingItem {
513 files: USER,
514 title: "Theme Mode",
515 description: "Choose a static, fixed theme or dynamically select themes based on appearance and light/dark modes.",
516 field: Box::new(SettingField {
517 organization_override: None,
518 json_path: Some("theme$"),
519 pick: |settings_content| {
520 Some(&dynamic_variants::<settings::ThemeSelection>()[
521 settings_content
522 .theme
523 .theme
524 .as_ref()?
525 .discriminant() as usize])
526 },
527 write: |settings_content, value, app: &App| {
528 let Some(value) = value else {
529 settings_content.theme.theme = None;
530 return;
531 };
532 let settings_value = settings_content.theme.theme.get_or_insert_default();
533 *settings_value = match value {
534 settings::ThemeSelectionDiscriminants::Static => {
535 let name = match settings_value {
536 settings::ThemeSelection::Static(_) => return,
537 settings::ThemeSelection::Dynamic { mode, light, dark } => {
538 match mode {
539 theme_settings::ThemeAppearanceMode::Light => light.clone(),
540 theme_settings::ThemeAppearanceMode::Dark => dark.clone(),
541 theme_settings::ThemeAppearanceMode::System => {
542 if SystemAppearance::global(app).is_light() {
543 light.clone()
544 } else {
545 dark.clone()
546 }
547 }
548 }
549 },
550 };
551 settings::ThemeSelection::Static(name)
552 },
553 settings::ThemeSelectionDiscriminants::Dynamic => {
554 let static_name = match settings_value {
555 settings::ThemeSelection::Static(theme_name) => theme_name.clone(),
556 settings::ThemeSelection::Dynamic {..} => return,
557 };
558
559 settings::ThemeSelection::Dynamic {
560 mode: settings::ThemeAppearanceMode::System,
561 light: static_name.clone(),
562 dark: static_name,
563 }
564 },
565 };
566 },
567 }),
568 metadata: None,
569 },
570 pick_discriminant: |settings_content| {
571 Some(settings_content.theme.theme.as_ref()?.discriminant() as usize)
572 },
573 fields: dynamic_variants::<settings::ThemeSelection>().into_iter().map(|variant| {
574 match variant {
575 settings::ThemeSelectionDiscriminants::Static => vec![
576 SettingItem {
577 files: USER,
578 title: "Theme Name",
579 description: "The name of your selected theme.",
580 field: Box::new(SettingField {
581 organization_override: None,
582 json_path: Some("theme"),
583 pick: |settings_content| {
584 match settings_content.theme.theme.as_ref() {
585 Some(settings::ThemeSelection::Static(name)) => Some(name),
586 _ => None
587 }
588 },
589 write: |settings_content, value, _| {
590 let Some(value) = value else {
591 return;
592 };
593 match settings_content
594 .theme
595 .theme.get_or_insert_default() {
596 settings::ThemeSelection::Static(theme_name) => *theme_name = value,
597 _ => return
598 }
599 },
600 }),
601 metadata: None,
602 }
603 ],
604 settings::ThemeSelectionDiscriminants::Dynamic => vec![
605 SettingItem {
606 files: USER,
607 title: "Mode",
608 description: "Choose whether to use the selected light or dark theme or to follow your OS appearance configuration.",
609 field: Box::new(SettingField {
610 organization_override: None,
611 json_path: Some("theme.mode"),
612 pick: |settings_content| {
613 match settings_content.theme.theme.as_ref() {
614 Some(settings::ThemeSelection::Dynamic { mode, ..}) => Some(mode),
615 _ => None
616 }
617 },
618 write: |settings_content, value, _| {
619 let Some(value) = value else {
620 return;
621 };
622 match settings_content
623 .theme
624 .theme.get_or_insert_default() {
625 settings::ThemeSelection::Dynamic{ mode, ..} => *mode = value,
626 _ => return
627 }
628 },
629 }),
630 metadata: None,
631 },
632 SettingItem {
633 files: USER,
634 title: "Light Theme",
635 description: "The theme to use when mode is set to light, or when mode is set to system and it is in light mode.",
636 field: Box::new(SettingField {
637 organization_override: None,
638 json_path: Some("theme.light"),
639 pick: |settings_content| {
640 match settings_content.theme.theme.as_ref() {
641 Some(settings::ThemeSelection::Dynamic { light, ..}) => Some(light),
642 _ => None
643 }
644 },
645 write: |settings_content, value, _| {
646 let Some(value) = value else {
647 return;
648 };
649 match settings_content
650 .theme
651 .theme.get_or_insert_default() {
652 settings::ThemeSelection::Dynamic{ light, ..} => *light = value,
653 _ => return
654 }
655 },
656 }),
657 metadata: None,
658 },
659 SettingItem {
660 files: USER,
661 title: "Dark Theme",
662 description: "The theme to use when mode is set to dark, or when mode is set to system and it is in dark mode.",
663 field: Box::new(SettingField {
664 organization_override: None,
665 json_path: Some("theme.dark"),
666 pick: |settings_content| {
667 match settings_content.theme.theme.as_ref() {
668 Some(settings::ThemeSelection::Dynamic { dark, ..}) => Some(dark),
669 _ => None
670 }
671 },
672 write: |settings_content, value, _| {
673 let Some(value) = value else {
674 return;
675 };
676 match settings_content
677 .theme
678 .theme.get_or_insert_default() {
679 settings::ThemeSelection::Dynamic{ dark, ..} => *dark = value,
680 _ => return
681 }
682 },
683 }),
684 metadata: None,
685 }
686 ],
687 }
688 }).collect(),
689 }),
690 SettingsPageItem::DynamicItem(DynamicItem {
691 discriminant: SettingItem {
692 files: USER,
693 title: "Icon Theme",
694 description: "The custom set of icons Omega will associate with files and directories.",
695 field: Box::new(SettingField {
696 organization_override: None,
697 json_path: Some("icon_theme$"),
698 pick: |settings_content| {
699 Some(&dynamic_variants::<settings::IconThemeSelection>()[
700 settings_content
701 .theme
702 .icon_theme
703 .as_ref()?
704 .discriminant() as usize])
705 },
706 write: |settings_content, value, app| {
707 let Some(value) = value else {
708 settings_content.theme.icon_theme = None;
709 return;
710 };
711 let settings_value = settings_content.theme.icon_theme.get_or_insert_with(|| {
712 settings::IconThemeSelection::Static(settings::IconThemeName(theme::default_icon_theme().name.clone().into()))
713 });
714 *settings_value = match value {
715 settings::IconThemeSelectionDiscriminants::Static => {
716 let name = match settings_value {
717 settings::IconThemeSelection::Static(_) => return,
718 settings::IconThemeSelection::Dynamic { mode, light, dark } => {
719 match mode {
720 theme_settings::ThemeAppearanceMode::Light => light.clone(),
721 theme_settings::ThemeAppearanceMode::Dark => dark.clone(),
722 theme_settings::ThemeAppearanceMode::System => {
723 if SystemAppearance::global(app).is_light() {
724 light.clone()
725 } else {
726 dark.clone()
727 }
728 }
729 }
730 },
731 };
732 settings::IconThemeSelection::Static(name)
733 },
734 settings::IconThemeSelectionDiscriminants::Dynamic => {
735 let static_name = match settings_value {
736 settings::IconThemeSelection::Static(theme_name) => theme_name.clone(),
737 settings::IconThemeSelection::Dynamic {..} => return,
738 };
739
740 settings::IconThemeSelection::Dynamic {
741 mode: settings::ThemeAppearanceMode::System,
742 light: static_name.clone(),
743 dark: static_name,
744 }
745 },
746 };
747 },
748 }),
749 metadata: None,
750 },
751 pick_discriminant: |settings_content| {
752 Some(settings_content.theme.icon_theme.as_ref()?.discriminant() as usize)
753 },
754 fields: dynamic_variants::<settings::IconThemeSelection>().into_iter().map(|variant| {
755 match variant {
756 settings::IconThemeSelectionDiscriminants::Static => vec![
757 SettingItem {
758 files: USER,
759 title: "Icon Theme Name",
760 description: "The name of your selected icon theme.",
761 field: Box::new(SettingField {
762 organization_override: None,
763 json_path: Some("icon_theme$string"),
764 pick: |settings_content| {
765 match settings_content.theme.icon_theme.as_ref() {
766 Some(settings::IconThemeSelection::Static(name)) => Some(name),
767 _ => None
768 }
769 },
770 write: |settings_content, value, _| {
771 let Some(value) = value else {
772 return;
773 };
774 match settings_content
775 .theme
776 .icon_theme.as_mut() {
777 Some(settings::IconThemeSelection::Static(theme_name)) => *theme_name = value,
778 _ => return
779 }
780 },
781 }),
782 metadata: None,
783 }
784 ],
785 settings::IconThemeSelectionDiscriminants::Dynamic => vec![
786 SettingItem {
787 files: USER,
788 title: "Mode",
789 description: "Choose whether to use the selected light or dark icon theme or to follow your OS appearance configuration.",
790 field: Box::new(SettingField {
791 organization_override: None,
792 json_path: Some("icon_theme"),
793 pick: |settings_content| {
794 match settings_content.theme.icon_theme.as_ref() {
795 Some(settings::IconThemeSelection::Dynamic { mode, ..}) => Some(mode),
796 _ => None
797 }
798 },
799 write: |settings_content, value, _| {
800 let Some(value) = value else {
801 return;
802 };
803 match settings_content
804 .theme
805 .icon_theme.as_mut() {
806 Some(settings::IconThemeSelection::Dynamic{ mode, ..}) => *mode = value,
807 _ => return
808 }
809 },
810 }),
811 metadata: None,
812 },
813 SettingItem {
814 files: USER,
815 title: "Light Icon Theme",
816 description: "The icon theme to use when mode is set to light, or when mode is set to system and it is in light mode.",
817 field: Box::new(SettingField {
818 organization_override: None,
819 json_path: Some("icon_theme.light"),
820 pick: |settings_content| {
821 match settings_content.theme.icon_theme.as_ref() {
822 Some(settings::IconThemeSelection::Dynamic { light, ..}) => Some(light),
823 _ => None
824 }
825 },
826 write: |settings_content, value, _| {
827 let Some(value) = value else {
828 return;
829 };
830 match settings_content
831 .theme
832 .icon_theme.as_mut() {
833 Some(settings::IconThemeSelection::Dynamic{ light, ..}) => *light = value,
834 _ => return
835 }
836 },
837 }),
838 metadata: None,
839 },
840 SettingItem {
841 files: USER,
842 title: "Dark Icon Theme",
843 description: "The icon theme to use when mode is set to dark, or when mode is set to system and it is in dark mode.",
844 field: Box::new(SettingField {
845 organization_override: None,
846 json_path: Some("icon_theme.dark"),
847 pick: |settings_content| {
848 match settings_content.theme.icon_theme.as_ref() {
849 Some(settings::IconThemeSelection::Dynamic { dark, ..}) => Some(dark),
850 _ => None
851 }
852 },
853 write: |settings_content, value, _| {
854 let Some(value) = value else {
855 return;
856 };
857 match settings_content
858 .theme
859 .icon_theme.as_mut() {
860 Some(settings::IconThemeSelection::Dynamic{ dark, ..}) => *dark = value,
861 _ => return
862 }
863 },
864 }),
865 metadata: None,
866 }
867 ],
868 }
869 }).collect(),
870 }),
871 ]
872 }
873
874 fn buffer_font_section() -> [SettingsPageItem; 7] {
875 [
876 SettingsPageItem::SectionHeader("Buffer Font"),
877 SettingsPageItem::SettingItem(SettingItem {
878 title: "Font Family",
879 description: "Font family for editor text.",
880 field: Box::new(SettingField {
881 organization_override: None,
882 json_path: Some("buffer_font_family"),
883 pick: |settings_content| settings_content.theme.buffer_font_family.as_ref(),
884 write: |settings_content, value, _| {
885 settings_content.theme.buffer_font_family = value;
886 },
887 }),
888 metadata: None,
889 files: USER,
890 }),
891 SettingsPageItem::SettingItem(SettingItem {
892 title: "Font Size",
893 description: "Font size for editor text.",
894 field: Box::new(SettingField {
895 organization_override: None,
896 json_path: Some("buffer_font_size"),
897 pick: |settings_content| settings_content.theme.buffer_font_size.as_ref(),
898 write: |settings_content, value, _| {
899 settings_content.theme.buffer_font_size = value;
900 },
901 }),
902 metadata: None,
903 files: USER,
904 }),
905 SettingsPageItem::SettingItem(SettingItem {
906 title: "Font Weight",
907 description: "Font weight for editor text (100-900).",
908 field: Box::new(SettingField {
909 organization_override: None,
910 json_path: Some("buffer_font_weight"),
911 pick: |settings_content| settings_content.theme.buffer_font_weight.as_ref(),
912 write: |settings_content, value, _| {
913 settings_content.theme.buffer_font_weight = value;
914 },
915 }),
916 metadata: None,
917 files: USER,
918 }),
919 SettingsPageItem::DynamicItem(DynamicItem {
920 discriminant: SettingItem {
921 files: USER,
922 title: "Line Height",
923 description: "Line height for editor text.",
924 field: Box::new(SettingField {
925 organization_override: None,
926 json_path: Some("buffer_line_height$"),
927 pick: |settings_content| {
928 Some(
929 &dynamic_variants::<settings::BufferLineHeight>()[settings_content
930 .theme
931 .buffer_line_height
932 .as_ref()?
933 .discriminant()
934 as usize],
935 )
936 },
937 write: |settings_content, value, _| {
938 let Some(value) = value else {
939 settings_content.theme.buffer_line_height = None;
940 return;
941 };
942 let settings_value = settings_content
943 .theme
944 .buffer_line_height
945 .get_or_insert_with(|| settings::BufferLineHeight::default());
946 *settings_value = match value {
947 settings::BufferLineHeightDiscriminants::Comfortable => {
948 settings::BufferLineHeight::Comfortable
949 }
950 settings::BufferLineHeightDiscriminants::Standard => {
951 settings::BufferLineHeight::Standard
952 }
953 settings::BufferLineHeightDiscriminants::Custom => {
954 let custom_value =
955 theme_settings::BufferLineHeight::from(*settings_value)
956 .value();
957 settings::BufferLineHeight::Custom(custom_value)
958 }
959 };
960 },
961 }),
962 metadata: None,
963 },
964 pick_discriminant: |settings_content| {
965 Some(
966 settings_content
967 .theme
968 .buffer_line_height
969 .as_ref()?
970 .discriminant() as usize,
971 )
972 },
973 fields: dynamic_variants::<settings::BufferLineHeight>()
974 .into_iter()
975 .map(|variant| match variant {
976 settings::BufferLineHeightDiscriminants::Comfortable => vec![],
977 settings::BufferLineHeightDiscriminants::Standard => vec![],
978 settings::BufferLineHeightDiscriminants::Custom => vec![SettingItem {
979 files: USER,
980 title: "Custom Line Height",
981 description: "Custom line height value (must be at least 1.0).",
982 field: Box::new(SettingField {
983 organization_override: None,
984 json_path: Some("buffer_line_height"),
985 pick: |settings_content| match settings_content
986 .theme
987 .buffer_line_height
988 .as_ref()
989 {
990 Some(settings::BufferLineHeight::Custom(value)) => Some(value),
991 _ => None,
992 },
993 write: |settings_content, value, _| {
994 let Some(value) = value else {
995 return;
996 };
997 match settings_content.theme.buffer_line_height.as_mut() {
998 Some(settings::BufferLineHeight::Custom(line_height)) => {
999 *line_height = f32::max(value, 1.0)
1000 }
1001 _ => return,
1002 }
1003 },
1004 }),
1005 metadata: None,
1006 }],
1007 })
1008 .collect(),
1009 }),
1010 SettingsPageItem::SettingItem(SettingItem {
1011 files: USER,
1012 title: "Font Features",
1013 description: "The OpenType features to enable for rendering in text buffers.",
1014 field: Box::new(
1015 SettingField {
1016 organization_override: None,
1017 json_path: Some("buffer_font_features"),
1018 pick: |settings_content| {
1019 settings_content.theme.buffer_font_features.as_ref()
1020 },
1021 write: |settings_content, value, _| {
1022 settings_content.theme.buffer_font_features = value;
1023 },
1024 }
1025 .unimplemented(),
1026 ),
1027 metadata: None,
1028 }),
1029 SettingsPageItem::SettingItem(SettingItem {
1030 files: USER,
1031 title: "Font Fallbacks",
1032 description: "The font fallbacks to use for rendering in text buffers.",
1033 field: Box::new(
1034 SettingField {
1035 organization_override: None,
1036 json_path: Some("buffer_font_fallbacks"),
1037 pick: |settings_content| {
1038 settings_content.theme.buffer_font_fallbacks.as_ref()
1039 },
1040 write: |settings_content, value, _| {
1041 settings_content.theme.buffer_font_fallbacks = value;
1042 },
1043 }
1044 .unimplemented(),
1045 ),
1046 metadata: None,
1047 }),
1048 ]
1049 }
1050
1051 fn ui_font_section() -> [SettingsPageItem; 6] {
1052 [
1053 SettingsPageItem::SectionHeader("UI Font"),
1054 SettingsPageItem::SettingItem(SettingItem {
1055 title: "Font Family",
1056 description: "Font family for UI elements.",
1057 field: Box::new(SettingField {
1058 organization_override: None,
1059 json_path: Some("ui_font_family"),
1060 pick: |settings_content| settings_content.theme.ui_font_family.as_ref(),
1061 write: |settings_content, value, _| {
1062 settings_content.theme.ui_font_family = value;
1063 },
1064 }),
1065 metadata: None,
1066 files: USER,
1067 }),
1068 SettingsPageItem::SettingItem(SettingItem {
1069 title: "Font Size",
1070 description: "Font size for UI elements.",
1071 field: Box::new(SettingField {
1072 organization_override: None,
1073 json_path: Some("ui_font_size"),
1074 pick: |settings_content| settings_content.theme.ui_font_size.as_ref(),
1075 write: |settings_content, value, _| {
1076 settings_content.theme.ui_font_size = value;
1077 },
1078 }),
1079 metadata: None,
1080 files: USER,
1081 }),
1082 SettingsPageItem::SettingItem(SettingItem {
1083 title: "Font Weight",
1084 description: "Font weight for UI elements (100-900).",
1085 field: Box::new(SettingField {
1086 organization_override: None,
1087 json_path: Some("ui_font_weight"),
1088 pick: |settings_content| settings_content.theme.ui_font_weight.as_ref(),
1089 write: |settings_content, value, _| {
1090 settings_content.theme.ui_font_weight = value;
1091 },
1092 }),
1093 metadata: None,
1094 files: USER,
1095 }),
1096 SettingsPageItem::SettingItem(SettingItem {
1097 files: USER,
1098 title: "Font Features",
1099 description: "The OpenType features to enable for rendering in UI elements.",
1100 field: Box::new(
1101 SettingField {
1102 organization_override: None,
1103 json_path: Some("ui_font_features"),
1104 pick: |settings_content| settings_content.theme.ui_font_features.as_ref(),
1105 write: |settings_content, value, _| {
1106 settings_content.theme.ui_font_features = value;
1107 },
1108 }
1109 .unimplemented(),
1110 ),
1111 metadata: None,
1112 }),
1113 SettingsPageItem::SettingItem(SettingItem {
1114 files: USER,
1115 title: "Font Fallbacks",
1116 description: "The font fallbacks to use for rendering in the UI.",
1117 field: Box::new(
1118 SettingField {
1119 organization_override: None,
1120 json_path: Some("ui_font_fallbacks"),
1121 pick: |settings_content| settings_content.theme.ui_font_fallbacks.as_ref(),
1122 write: |settings_content, value, _| {
1123 settings_content.theme.ui_font_fallbacks = value;
1124 },
1125 }
1126 .unimplemented(),
1127 ),
1128 metadata: None,
1129 }),
1130 ]
1131 }
1132
1133 fn agent_panel_font_section() -> [SettingsPageItem; 3] {
1134 [
1135 SettingsPageItem::SectionHeader("Agent Panel Font"),
1136 SettingsPageItem::SettingItem(SettingItem {
1137 title: "UI Font Size",
1138 description: "Font size for agent response text in the agent panel. Falls back to the regular UI font size.",
1139 field: Box::new(SettingField {
1140 organization_override: None,
1141 json_path: Some("agent_ui_font_size"),
1142 pick: |settings_content| {
1143 settings_content
1144 .theme
1145 .agent_ui_font_size
1146 .as_ref()
1147 .or(settings_content.theme.ui_font_size.as_ref())
1148 },
1149 write: |settings_content, value, _| {
1150 settings_content.theme.agent_ui_font_size = value;
1151 },
1152 }),
1153 metadata: None,
1154 files: USER,
1155 }),
1156 SettingsPageItem::SettingItem(SettingItem {
1157 title: "Buffer Font Size",
1158 description: "Font size for user messages text in the agent panel.",
1159 field: Box::new(SettingField {
1160 organization_override: None,
1161 json_path: Some("agent_buffer_font_size"),
1162 pick: |settings_content| {
1163 settings_content
1164 .theme
1165 .agent_buffer_font_size
1166 .as_ref()
1167 .or(settings_content.theme.buffer_font_size.as_ref())
1168 },
1169 write: |settings_content, value, _| {
1170 settings_content.theme.agent_buffer_font_size = value;
1171 },
1172 }),
1173 metadata: None,
1174 files: USER,
1175 }),
1176 ]
1177 }
1178
1179 fn markdown_preview_font_section() -> [SettingsPageItem; 4] {
1180 [
1181 SettingsPageItem::SectionHeader("Markdown Preview Font"),
1182 SettingsPageItem::SettingItem(SettingItem {
1183 title: "Font Family",
1184 description: "Font family for the markdown preview. Falls back to the UI font family.",
1185 field: Box::new(SettingField {
1186 organization_override: None,
1187 json_path: Some("markdown_preview_font_family"),
1188 pick: |settings_content| {
1189 settings_content.theme.markdown_preview_font_family.as_ref()
1190 },
1191 write: |settings_content, value, _| {
1192 settings_content.theme.markdown_preview_font_family = value;
1193 },
1194 }),
1195 metadata: None,
1196 files: USER,
1197 }),
1198 SettingsPageItem::SettingItem(SettingItem {
1199 title: "Code Font Family",
1200 description: "Font family for code blocks in the markdown preview. Falls back to the editor font family.",
1201 field: Box::new(SettingField {
1202 organization_override: None,
1203 json_path: Some("markdown_preview_code_font_family"),
1204 pick: |settings_content| {
1205 settings_content
1206 .theme
1207 .markdown_preview_code_font_family
1208 .as_ref()
1209 },
1210 write: |settings_content, value, _| {
1211 settings_content.theme.markdown_preview_code_font_family = value;
1212 },
1213 }),
1214 metadata: None,
1215 files: USER,
1216 }),
1217 SettingsPageItem::SettingItem(SettingItem {
1218 title: "Font Size",
1219 description: "Font size for the markdown preview. Falls back to the editor font size.",
1220 field: Box::new(SettingField {
1221 organization_override: None,
1222 json_path: Some("markdown_preview_font_size"),
1223 pick: |settings_content| {
1224 settings_content
1225 .theme
1226 .markdown_preview_font_size
1227 .as_ref()
1228 .or(settings_content.theme.buffer_font_size.as_ref())
1229 },
1230 write: |settings_content, value, _| {
1231 settings_content.theme.markdown_preview_font_size = value;
1232 },
1233 }),
1234 metadata: None,
1235 files: USER,
1236 }),
1237 ]
1238 }
1239
1240 fn text_rendering_section() -> [SettingsPageItem; 2] {
1241 [
1242 SettingsPageItem::SectionHeader("Text Rendering"),
1243 SettingsPageItem::SettingItem(SettingItem {
1244 title: "Text Rendering Mode",
1245 description: "The text rendering mode to use.",
1246 field: Box::new(SettingField {
1247 organization_override: None,
1248 json_path: Some("text_rendering_mode"),
1249 pick: |settings_content| {
1250 settings_content.workspace.text_rendering_mode.as_ref()
1251 },
1252 write: |settings_content, value, _| {
1253 settings_content.workspace.text_rendering_mode = value;
1254 },
1255 }),
1256 metadata: None,
1257 files: USER,
1258 }),
1259 ]
1260 }
1261
1262 fn cursor_section() -> [SettingsPageItem; 6] {
1263 [
1264 SettingsPageItem::SectionHeader("Cursor"),
1265 SettingsPageItem::SettingItem(SettingItem {
1266 title: "Multi Cursor Modifier",
1267 description: "Modifier key for adding multiple cursors.",
1268 field: Box::new(SettingField {
1269 organization_override: None,
1270 json_path: Some("multi_cursor_modifier"),
1271 pick: |settings_content| settings_content.editor.multi_cursor_modifier.as_ref(),
1272 write: |settings_content, value, _| {
1273 settings_content.editor.multi_cursor_modifier = value;
1274 },
1275 }),
1276 metadata: None,
1277 files: USER,
1278 }),
1279 SettingsPageItem::SettingItem(SettingItem {
1280 title: "Cursor Blink",
1281 description: "Whether the cursor blinks in the editor.",
1282 field: Box::new(SettingField {
1283 organization_override: None,
1284 json_path: Some("cursor_blink"),
1285 pick: |settings_content| settings_content.editor.cursor_blink.as_ref(),
1286 write: |settings_content, value, _| {
1287 settings_content.editor.cursor_blink = value;
1288 },
1289 }),
1290 metadata: None,
1291 files: USER,
1292 }),
1293 SettingsPageItem::SettingItem(SettingItem {
1294 title: "Cursor Shape",
1295 description: "Cursor shape for the editor.",
1296 field: Box::new(SettingField {
1297 organization_override: None,
1298 json_path: Some("cursor_shape"),
1299 pick: |settings_content| settings_content.editor.cursor_shape.as_ref(),
1300 write: |settings_content, value, _| {
1301 settings_content.editor.cursor_shape = value;
1302 },
1303 }),
1304 metadata: None,
1305 files: USER,
1306 }),
1307 SettingsPageItem::SettingItem(SettingItem {
1308 title: "Hide Mouse",
1309 description: "When to hide the mouse cursor.",
1310 field: Box::new(SettingField {
1311 organization_override: None,
1312 json_path: Some("hide_mouse"),
1313 pick: |settings_content| settings_content.hide_mouse.as_ref(),
1314 write: |settings_content, value, _| {
1315 settings_content.hide_mouse = value;
1316 },
1317 }),
1318 metadata: None,
1319 files: USER,
1320 }),
1321 SettingsPageItem::SettingItem(SettingItem {
1322 title: "Reduce Motion",
1323 description: "Whether to reduce non-essential motion, such as loading spinners, by rendering them in a static state.",
1324 field: Box::new(SettingField {
1325 organization_override: None,
1326 json_path: Some("reduce_motion"),
1327 pick: |settings_content| settings_content.reduce_motion.as_ref(),
1328 write: |settings_content, value, _| {
1329 settings_content.reduce_motion = value;
1330 },
1331 }),
1332 metadata: None,
1333 files: USER,
1334 }),
1335 ]
1336 }
1337
1338 fn highlighting_section() -> [SettingsPageItem; 6] {
1339 [
1340 SettingsPageItem::SectionHeader("Highlighting"),
1341 SettingsPageItem::SettingItem(SettingItem {
1342 title: "Unnecessary Code Fade",
1343 description: "How much to fade out unused code (0.0 - 0.9).",
1344 field: Box::new(SettingField {
1345 organization_override: None,
1346 json_path: Some("unnecessary_code_fade"),
1347 pick: |settings_content| settings_content.theme.unnecessary_code_fade.as_ref(),
1348 write: |settings_content, value, _| {
1349 settings_content.theme.unnecessary_code_fade = value;
1350 },
1351 }),
1352 metadata: None,
1353 files: USER,
1354 }),
1355 SettingsPageItem::SettingItem(SettingItem {
1356 title: "Current Line Highlight",
1357 description: "How to highlight the current line.",
1358 field: Box::new(SettingField {
1359 organization_override: None,
1360 json_path: Some("current_line_highlight"),
1361 pick: |settings_content| {
1362 settings_content.editor.current_line_highlight.as_ref()
1363 },
1364 write: |settings_content, value, _| {
1365 settings_content.editor.current_line_highlight = value;
1366 },
1367 }),
1368 metadata: None,
1369 files: USER,
1370 }),
1371 SettingsPageItem::SettingItem(SettingItem {
1372 title: "Selection Highlight",
1373 description: "Highlight all occurrences of selected text.",
1374 field: Box::new(SettingField {
1375 organization_override: None,
1376 json_path: Some("selection_highlight"),
1377 pick: |settings_content| settings_content.editor.selection_highlight.as_ref(),
1378 write: |settings_content, value, _| {
1379 settings_content.editor.selection_highlight = value;
1380 },
1381 }),
1382 metadata: None,
1383 files: USER,
1384 }),
1385 SettingsPageItem::SettingItem(SettingItem {
1386 title: "Rounded Selection",
1387 description: "Whether the text selection should have rounded corners.",
1388 field: Box::new(SettingField {
1389 organization_override: None,
1390 json_path: Some("rounded_selection"),
1391 pick: |settings_content| settings_content.editor.rounded_selection.as_ref(),
1392 write: |settings_content, value, _| {
1393 settings_content.editor.rounded_selection = value;
1394 },
1395 }),
1396 metadata: None,
1397 files: USER,
1398 }),
1399 SettingsPageItem::SettingItem(SettingItem {
1400 title: "Minimum Contrast For Highlights",
1401 description: "The minimum APCA perceptual contrast to maintain when rendering text over highlight backgrounds.",
1402 field: Box::new(SettingField {
1403 organization_override: None,
1404 json_path: Some("minimum_contrast_for_highlights"),
1405 pick: |settings_content| {
1406 settings_content
1407 .editor
1408 .minimum_contrast_for_highlights
1409 .as_ref()
1410 },
1411 write: |settings_content, value, _| {
1412 settings_content.editor.minimum_contrast_for_highlights = value;
1413 },
1414 }),
1415 metadata: None,
1416 files: USER,
1417 }),
1418 ]
1419 }
1420
1421 fn guides_section() -> [SettingsPageItem; 3] {
1422 [
1423 SettingsPageItem::SectionHeader("Guides"),
1424 SettingsPageItem::SettingItem(SettingItem {
1425 title: "Show Wrap Guides",
1426 description: "Show wrap guides (vertical rulers).",
1427 field: Box::new(SettingField {
1428 organization_override: None,
1429 json_path: Some("show_wrap_guides"),
1430 pick: |settings_content| {
1431 settings_content
1432 .project
1433 .all_languages
1434 .defaults
1435 .show_wrap_guides
1436 .as_ref()
1437 },
1438 write: |settings_content, value, _| {
1439 settings_content
1440 .project
1441 .all_languages
1442 .defaults
1443 .show_wrap_guides = value;
1444 },
1445 }),
1446 metadata: None,
1447 files: USER | PROJECT,
1448 }),
1449 // todo(settings_ui): This needs a custom component
1450 SettingsPageItem::SettingItem(SettingItem {
1451 title: "Wrap Guides",
1452 description: "Character counts at which to show wrap guides.",
1453 field: Box::new(
1454 SettingField {
1455 organization_override: None,
1456 json_path: Some("wrap_guides"),
1457 pick: |settings_content| {
1458 settings_content
1459 .project
1460 .all_languages
1461 .defaults
1462 .wrap_guides
1463 .as_ref()
1464 },
1465 write: |settings_content, value, _| {
1466 settings_content.project.all_languages.defaults.wrap_guides = value;
1467 },
1468 }
1469 .unimplemented(),
1470 ),
1471 metadata: None,
1472 files: USER | PROJECT,
1473 }),
1474 ]
1475 }
1476
1477 let items: Box<[SettingsPageItem]> = concat_sections!(
1478 theme_section(),
1479 buffer_font_section(),
1480 ui_font_section(),
1481 agent_panel_font_section(),
1482 markdown_preview_font_section(),
1483 text_rendering_section(),
1484 cursor_section(),
1485 highlighting_section(),
1486 guides_section(),
1487 );
1488
1489 SettingsPage {
1490 title: "Appearance",
1491 items,
1492 }
1493}
1494
1495fn keymap_page() -> SettingsPage {
1496 fn keybindings_section() -> [SettingsPageItem; 2] {
1497 [
1498 SettingsPageItem::SectionHeader("Keybindings"),
1499 SettingsPageItem::ActionLink(ActionLink {
1500 title: "Edit Keybindings".into(),
1501 description: Some("Customize keybindings in the keymap editor.".into()),
1502 button_text: "Open Keymap".into(),
1503 on_click: Arc::new(|settings_window, window, cx| {
1504 let Some(original_window) = settings_window.original_window else {
1505 return;
1506 };
1507 original_window
1508 .update(cx, |_workspace, original_window, cx| {
1509 original_window
1510 .dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx);
1511 original_window.activate_window();
1512 })
1513 .ok();
1514 window.remove_window();
1515 }),
1516 files: USER,
1517 }),
1518 ]
1519 }
1520
1521 fn base_keymap_section() -> [SettingsPageItem; 2] {
1522 [
1523 SettingsPageItem::SectionHeader("Base Keymap"),
1524 SettingsPageItem::SettingItem(SettingItem {
1525 title: "Base Keymap",
1526 description: "The name of a base set of key bindings to use.",
1527 field: Box::new(SettingField {
1528 organization_override: None,
1529 json_path: Some("base_keymap"),
1530 pick: |settings_content| settings_content.base_keymap.as_ref(),
1531 write: |settings_content, value, _| {
1532 settings_content.base_keymap = value;
1533 },
1534 }),
1535 metadata: Some(Box::new(SettingsFieldMetadata {
1536 should_do_titlecase: Some(false),
1537 ..Default::default()
1538 })),
1539 files: USER,
1540 }),
1541 ]
1542 }
1543
1544 fn modal_editing_section() -> [SettingsPageItem; 3] {
1545 [
1546 SettingsPageItem::SectionHeader("Modal Editing"),
1547 SettingsPageItem::SettingItem(SettingItem {
1548 title: "Vim Mode",
1549 description: "Enable Vim mode and key bindings.",
1550 field: Box::new(SettingField {
1551 organization_override: None,
1552 json_path: Some("vim_mode"),
1553 pick: |settings_content| settings_content.vim_mode.as_ref(),
1554 write: write_vim_mode,
1555 }),
1556 metadata: None,
1557 files: USER,
1558 }),
1559 SettingsPageItem::SettingItem(SettingItem {
1560 title: "Helix Mode",
1561 description: "Enable Helix mode and key bindings.",
1562 field: Box::new(SettingField {
1563 organization_override: None,
1564 json_path: Some("helix_mode"),
1565 pick: |settings_content| settings_content.helix_mode.as_ref(),
1566 write: write_helix_mode,
1567 }),
1568 metadata: None,
1569 files: USER,
1570 }),
1571 ]
1572 }
1573
1574 let items: Box<[SettingsPageItem]> = concat_sections!(
1575 keybindings_section(),
1576 base_keymap_section(),
1577 modal_editing_section(),
1578 );
1579
1580 SettingsPage {
1581 title: "Keymap",
1582 items,
1583 }
1584}
1585
1586fn editor_page() -> SettingsPage {
1587 fn auto_save_section() -> [SettingsPageItem; 2] {
1588 [
1589 SettingsPageItem::SectionHeader("Auto Save"),
1590 SettingsPageItem::DynamicItem(DynamicItem {
1591 discriminant: SettingItem {
1592 files: USER,
1593 title: "Auto Save Mode",
1594 description: "When to auto save buffer changes.",
1595 field: Box::new(SettingField {
1596 organization_override: None,
1597 json_path: Some("autosave$"),
1598 pick: |settings_content| {
1599 Some(
1600 &dynamic_variants::<settings::AutosaveSetting>()[settings_content
1601 .workspace
1602 .autosave
1603 .as_ref()?
1604 .discriminant()
1605 as usize],
1606 )
1607 },
1608 write: |settings_content, value, _| {
1609 let Some(value) = value else {
1610 settings_content.workspace.autosave = None;
1611 return;
1612 };
1613 let settings_value = settings_content
1614 .workspace
1615 .autosave
1616 .get_or_insert_with(|| settings::AutosaveSetting::Off);
1617 *settings_value = match value {
1618 settings::AutosaveSettingDiscriminants::Off => {
1619 settings::AutosaveSetting::Off
1620 }
1621 settings::AutosaveSettingDiscriminants::AfterDelay => {
1622 let milliseconds = match settings_value {
1623 settings::AutosaveSetting::AfterDelay { milliseconds } => {
1624 *milliseconds
1625 }
1626 _ => settings::DelayMs(1000),
1627 };
1628 settings::AutosaveSetting::AfterDelay { milliseconds }
1629 }
1630 settings::AutosaveSettingDiscriminants::OnFocusChange => {
1631 settings::AutosaveSetting::OnFocusChange
1632 }
1633 settings::AutosaveSettingDiscriminants::OnWindowChange => {
1634 settings::AutosaveSetting::OnWindowChange
1635 }
1636 };
1637 },
1638 }),
1639 metadata: None,
1640 },
1641 pick_discriminant: |settings_content| {
1642 Some(settings_content.workspace.autosave.as_ref()?.discriminant() as usize)
1643 },
1644 fields: dynamic_variants::<settings::AutosaveSetting>()
1645 .into_iter()
1646 .map(|variant| match variant {
1647 settings::AutosaveSettingDiscriminants::Off => vec![],
1648 settings::AutosaveSettingDiscriminants::AfterDelay => vec![SettingItem {
1649 files: USER,
1650 title: "Delay (milliseconds)",
1651 description: "Save after inactivity period (in milliseconds).",
1652 field: Box::new(SettingField {
1653 organization_override: None,
1654 json_path: Some("autosave.after_delay.milliseconds"),
1655 pick: |settings_content| match settings_content
1656 .workspace
1657 .autosave
1658 .as_ref()
1659 {
1660 Some(settings::AutosaveSetting::AfterDelay {
1661 milliseconds,
1662 }) => Some(milliseconds),
1663 _ => None,
1664 },
1665 write: |settings_content, value, _| {
1666 let Some(value) = value else {
1667 settings_content.workspace.autosave = None;
1668 return;
1669 };
1670 match settings_content.workspace.autosave.as_mut() {
1671 Some(settings::AutosaveSetting::AfterDelay {
1672 milliseconds,
1673 }) => *milliseconds = value,
1674 _ => return,
1675 }
1676 },
1677 }),
1678 metadata: None,
1679 }],
1680 settings::AutosaveSettingDiscriminants::OnFocusChange => vec![],
1681 settings::AutosaveSettingDiscriminants::OnWindowChange => vec![],
1682 })
1683 .collect(),
1684 }),
1685 ]
1686 }
1687
1688 fn which_key_section() -> [SettingsPageItem; 3] {
1689 [
1690 SettingsPageItem::SectionHeader("Which-key Menu"),
1691 SettingsPageItem::SettingItem(SettingItem {
1692 title: "Show Which-key Menu",
1693 description: "Display the which-key menu with matching bindings while a multi-stroke binding is pending.",
1694 field: Box::new(SettingField {
1695 organization_override: None,
1696 json_path: Some("which_key.enabled"),
1697 pick: |settings_content| {
1698 settings_content
1699 .which_key
1700 .as_ref()
1701 .and_then(|settings| settings.enabled.as_ref())
1702 },
1703 write: |settings_content, value, _| {
1704 settings_content.which_key.get_or_insert_default().enabled = value;
1705 },
1706 }),
1707 metadata: None,
1708 files: USER,
1709 }),
1710 SettingsPageItem::SettingItem(SettingItem {
1711 title: "Menu Delay",
1712 description: "Delay in milliseconds before the which-key menu appears.",
1713 field: Box::new(SettingField {
1714 organization_override: None,
1715 json_path: Some("which_key.delay_ms"),
1716 pick: |settings_content| {
1717 settings_content
1718 .which_key
1719 .as_ref()
1720 .and_then(|settings| settings.delay_ms.as_ref())
1721 },
1722 write: |settings_content, value, _| {
1723 settings_content.which_key.get_or_insert_default().delay_ms = value;
1724 },
1725 }),
1726 metadata: None,
1727 files: USER,
1728 }),
1729 ]
1730 }
1731
1732 fn multibuffer_section() -> [SettingsPageItem; 7] {
1733 [
1734 SettingsPageItem::SectionHeader("Multibuffer"),
1735 SettingsPageItem::SettingItem(SettingItem {
1736 title: "Double Click In Multibuffer",
1737 description: "What to do when multibuffer is double-clicked in some of its excerpts.",
1738 field: Box::new(SettingField {
1739 organization_override: None,
1740 json_path: Some("double_click_in_multibuffer"),
1741 pick: |settings_content| {
1742 settings_content.editor.double_click_in_multibuffer.as_ref()
1743 },
1744 write: |settings_content, value, _| {
1745 settings_content.editor.double_click_in_multibuffer = value;
1746 },
1747 }),
1748 metadata: None,
1749 files: USER,
1750 }),
1751 SettingsPageItem::SettingItem(SettingItem {
1752 title: "Expand Excerpt Lines",
1753 description: "How many lines to expand the multibuffer excerpts by default.",
1754 field: Box::new(SettingField {
1755 organization_override: None,
1756 json_path: Some("expand_excerpt_lines"),
1757 pick: |settings_content| settings_content.editor.expand_excerpt_lines.as_ref(),
1758 write: |settings_content, value, _| {
1759 settings_content.editor.expand_excerpt_lines = value;
1760 },
1761 }),
1762 metadata: None,
1763 files: USER,
1764 }),
1765 SettingsPageItem::SettingItem(SettingItem {
1766 title: "Excerpt Context Lines",
1767 description: "How many lines of context to provide in multibuffer excerpts by default.",
1768 field: Box::new(SettingField {
1769 organization_override: None,
1770 json_path: Some("excerpt_context_lines"),
1771 pick: |settings_content| settings_content.editor.excerpt_context_lines.as_ref(),
1772 write: |settings_content, value, _| {
1773 settings_content.editor.excerpt_context_lines = value;
1774 },
1775 }),
1776 metadata: None,
1777 files: USER,
1778 }),
1779 SettingsPageItem::SettingItem(SettingItem {
1780 title: "Expand Outlines With Depth",
1781 description: "Default depth to expand outline items in the current file.",
1782 field: Box::new(SettingField {
1783 organization_override: None,
1784 json_path: Some("outline_panel.expand_outlines_with_depth"),
1785 pick: |settings_content| {
1786 settings_content
1787 .outline_panel
1788 .as_ref()
1789 .and_then(|outline_panel| {
1790 outline_panel.expand_outlines_with_depth.as_ref()
1791 })
1792 },
1793 write: |settings_content, value, _| {
1794 settings_content
1795 .outline_panel
1796 .get_or_insert_default()
1797 .expand_outlines_with_depth = value;
1798 },
1799 }),
1800 metadata: None,
1801 files: USER,
1802 }),
1803 SettingsPageItem::SettingItem(SettingItem {
1804 title: "Diff View Style",
1805 description: "How to display diffs in the editor.",
1806 field: Box::new(SettingField {
1807 organization_override: None,
1808 json_path: Some("diff_view_style"),
1809 pick: |settings_content| settings_content.editor.diff_view_style.as_ref(),
1810 write: |settings_content, value, _| {
1811 settings_content.editor.diff_view_style = value;
1812 },
1813 }),
1814 metadata: None,
1815 files: USER,
1816 }),
1817 SettingsPageItem::SettingItem(SettingItem {
1818 title: "Minimum Split Diff Width",
1819 description: "The minimum width (in columns) at which the split diff view is used. When the editor is narrower, the diff view automatically switches to unified mode. Set to 0 to disable.",
1820 field: Box::new(SettingField {
1821 organization_override: None,
1822 json_path: Some("minimum_split_diff_width"),
1823 pick: |settings_content| {
1824 settings_content.editor.minimum_split_diff_width.as_ref()
1825 },
1826 write: |settings_content, value, _| {
1827 settings_content.editor.minimum_split_diff_width = value;
1828 },
1829 }),
1830 metadata: None,
1831 files: USER,
1832 }),
1833 ]
1834 }
1835
1836 fn scrolling_section() -> [SettingsPageItem; 9] {
1837 [
1838 SettingsPageItem::SectionHeader("Scrolling"),
1839 SettingsPageItem::SettingItem(SettingItem {
1840 title: "Scroll Beyond Last Line",
1841 description: "Whether the editor will scroll beyond the last line.",
1842 field: Box::new(SettingField {
1843 organization_override: None,
1844 json_path: Some("scroll_beyond_last_line"),
1845 pick: |settings_content| {
1846 settings_content.editor.scroll_beyond_last_line.as_ref()
1847 },
1848 write: |settings_content, value, _| {
1849 settings_content.editor.scroll_beyond_last_line = value;
1850 },
1851 }),
1852 metadata: None,
1853 files: USER,
1854 }),
1855 SettingsPageItem::SettingItem(SettingItem {
1856 title: "Vertical Scroll Margin",
1857 description: "The number of lines to keep above/below the cursor when auto-scrolling.",
1858 field: Box::new(SettingField {
1859 organization_override: None,
1860 json_path: Some("vertical_scroll_margin"),
1861 pick: |settings_content| {
1862 settings_content.editor.vertical_scroll_margin.as_ref()
1863 },
1864 write: |settings_content, value, _| {
1865 settings_content.editor.vertical_scroll_margin = value;
1866 },
1867 }),
1868 metadata: None,
1869 files: USER,
1870 }),
1871 SettingsPageItem::SettingItem(SettingItem {
1872 title: "Horizontal Scroll Margin",
1873 description: "The number of characters to keep on either side when scrolling with the mouse.",
1874 field: Box::new(SettingField {
1875 organization_override: None,
1876 json_path: Some("horizontal_scroll_margin"),
1877 pick: |settings_content| {
1878 settings_content.editor.horizontal_scroll_margin.as_ref()
1879 },
1880 write: |settings_content, value, _| {
1881 settings_content.editor.horizontal_scroll_margin = value;
1882 },
1883 }),
1884 metadata: None,
1885 files: USER,
1886 }),
1887 SettingsPageItem::SettingItem(SettingItem {
1888 title: "Scroll Sensitivity",
1889 description: "Scroll sensitivity multiplier for both horizontal and vertical scrolling.",
1890 field: Box::new(SettingField {
1891 organization_override: None,
1892 json_path: Some("scroll_sensitivity"),
1893 pick: |settings_content| settings_content.editor.scroll_sensitivity.as_ref(),
1894 write: |settings_content, value, _| {
1895 settings_content.editor.scroll_sensitivity = value;
1896 },
1897 }),
1898 metadata: None,
1899 files: USER,
1900 }),
1901 SettingsPageItem::SettingItem(SettingItem {
1902 title: "Mouse Wheel Zoom",
1903 description: "Whether to zoom the editor font size with the mouse wheel while holding the primary modifier key.",
1904 field: Box::new(SettingField {
1905 organization_override: None,
1906 json_path: Some("mouse_wheel_zoom"),
1907 pick: |settings_content| settings_content.editor.mouse_wheel_zoom.as_ref(),
1908 write: |settings_content, value, _| {
1909 settings_content.editor.mouse_wheel_zoom = value;
1910 },
1911 }),
1912 metadata: None,
1913 files: USER,
1914 }),
1915 SettingsPageItem::SettingItem(SettingItem {
1916 title: "Fast Scroll Sensitivity",
1917 description: "Fast scroll sensitivity multiplier for both horizontal and vertical scrolling.",
1918 field: Box::new(SettingField {
1919 organization_override: None,
1920 json_path: Some("fast_scroll_sensitivity"),
1921 pick: |settings_content| {
1922 settings_content.editor.fast_scroll_sensitivity.as_ref()
1923 },
1924 write: |settings_content, value, _| {
1925 settings_content.editor.fast_scroll_sensitivity = value;
1926 },
1927 }),
1928 metadata: None,
1929 files: USER,
1930 }),
1931 SettingsPageItem::SettingItem(SettingItem {
1932 title: "Autoscroll On Clicks",
1933 description: "Whether to scroll when clicking near the edge of the visible text area.",
1934 field: Box::new(SettingField {
1935 organization_override: None,
1936 json_path: Some("autoscroll_on_clicks"),
1937 pick: |settings_content| settings_content.editor.autoscroll_on_clicks.as_ref(),
1938 write: |settings_content, value, _| {
1939 settings_content.editor.autoscroll_on_clicks = value;
1940 },
1941 }),
1942 metadata: None,
1943 files: USER,
1944 }),
1945 SettingsPageItem::SettingItem(SettingItem {
1946 title: "Sticky Scroll",
1947 description: "Whether to stick scopes to the top of the editor",
1948 field: Box::new(SettingField {
1949 organization_override: None,
1950 json_path: Some("sticky_scroll.enabled"),
1951 pick: |settings_content| {
1952 settings_content
1953 .editor
1954 .sticky_scroll
1955 .as_ref()
1956 .and_then(|sticky_scroll| sticky_scroll.enabled.as_ref())
1957 },
1958 write: |settings_content, value, _| {
1959 settings_content
1960 .editor
1961 .sticky_scroll
1962 .get_or_insert_default()
1963 .enabled = value;
1964 },
1965 }),
1966 metadata: None,
1967 files: USER,
1968 }),
1969 ]
1970 }
1971
1972 fn signature_help_section() -> [SettingsPageItem; 4] {
1973 [
1974 SettingsPageItem::SectionHeader("Signature Help"),
1975 SettingsPageItem::SettingItem(SettingItem {
1976 title: "Auto Signature Help",
1977 description: "Automatically show a signature help pop-up.",
1978 field: Box::new(SettingField {
1979 organization_override: None,
1980 json_path: Some("auto_signature_help"),
1981 pick: |settings_content| settings_content.editor.auto_signature_help.as_ref(),
1982 write: |settings_content, value, _| {
1983 settings_content.editor.auto_signature_help = value;
1984 },
1985 }),
1986 metadata: None,
1987 files: USER,
1988 }),
1989 SettingsPageItem::SettingItem(SettingItem {
1990 title: "Show Signature Help After Edits",
1991 description: "Show the signature help pop-up after completions or bracket pairs are inserted.",
1992 field: Box::new(SettingField {
1993 organization_override: None,
1994 json_path: Some("show_signature_help_after_edits"),
1995 pick: |settings_content| {
1996 settings_content
1997 .editor
1998 .show_signature_help_after_edits
1999 .as_ref()
2000 },
2001 write: |settings_content, value, _| {
2002 settings_content.editor.show_signature_help_after_edits = value;
2003 },
2004 }),
2005 metadata: None,
2006 files: USER,
2007 }),
2008 SettingsPageItem::SettingItem(SettingItem {
2009 title: "Snippet Sort Order",
2010 description: "Determines how snippets are sorted relative to other completion items.",
2011 field: Box::new(SettingField {
2012 organization_override: None,
2013 json_path: Some("snippet_sort_order"),
2014 pick: |settings_content| settings_content.editor.snippet_sort_order.as_ref(),
2015 write: |settings_content, value, _| {
2016 settings_content.editor.snippet_sort_order = value;
2017 },
2018 }),
2019 metadata: None,
2020 files: USER,
2021 }),
2022 ]
2023 }
2024
2025 fn hover_popover_section() -> [SettingsPageItem; 5] {
2026 [
2027 SettingsPageItem::SectionHeader("Hover Popover"),
2028 SettingsPageItem::SettingItem(SettingItem {
2029 title: "Enabled",
2030 description: "Show the informational hover box when moving the mouse over symbols in the editor.",
2031 field: Box::new(SettingField {
2032 organization_override: None,
2033 json_path: Some("hover_popover_enabled"),
2034 pick: |settings_content| settings_content.editor.hover_popover_enabled.as_ref(),
2035 write: |settings_content, value, _| {
2036 settings_content.editor.hover_popover_enabled = value;
2037 },
2038 }),
2039 metadata: None,
2040 files: USER,
2041 }),
2042 // todo(settings ui): add units to this number input
2043 SettingsPageItem::SettingItem(SettingItem {
2044 title: "Delay",
2045 description: "Time to wait in milliseconds before showing the informational hover box.",
2046 field: Box::new(SettingField {
2047 organization_override: None,
2048 json_path: Some("hover_popover_delay"),
2049 pick: |settings_content| settings_content.editor.hover_popover_delay.as_ref(),
2050 write: |settings_content, value, _| {
2051 settings_content.editor.hover_popover_delay = value;
2052 },
2053 }),
2054 metadata: None,
2055 files: USER,
2056 }),
2057 SettingsPageItem::SettingItem(SettingItem {
2058 title: "Sticky",
2059 description: "Whether the hover popover sticks when the mouse moves toward it, allowing interaction with its contents.",
2060 field: Box::new(SettingField {
2061 organization_override: None,
2062 json_path: Some("hover_popover_sticky"),
2063 pick: |settings_content| settings_content.editor.hover_popover_sticky.as_ref(),
2064 write: |settings_content, value, _| {
2065 settings_content.editor.hover_popover_sticky = value;
2066 },
2067 }),
2068 metadata: None,
2069 files: USER,
2070 }),
2071 // todo(settings ui): add units to this number input
2072 SettingsPageItem::SettingItem(SettingItem {
2073 title: "Hiding Delay",
2074 description: "Time to wait in milliseconds before hiding the hover popover after the mouse moves away.",
2075 field: Box::new(SettingField {
2076 organization_override: None,
2077 json_path: Some("hover_popover_hiding_delay"),
2078 pick: |settings_content| {
2079 settings_content.editor.hover_popover_hiding_delay.as_ref()
2080 },
2081 write: |settings_content, value, _| {
2082 settings_content.editor.hover_popover_hiding_delay = value;
2083 },
2084 }),
2085 metadata: None,
2086 files: USER,
2087 }),
2088 ]
2089 }
2090
2091 fn drag_and_drop_selection_section() -> [SettingsPageItem; 3] {
2092 [
2093 SettingsPageItem::SectionHeader("Drag And Drop Selection"),
2094 SettingsPageItem::SettingItem(SettingItem {
2095 title: "Enabled",
2096 description: "Enable drag and drop selection.",
2097 field: Box::new(SettingField {
2098 organization_override: None,
2099 json_path: Some("drag_and_drop_selection.enabled"),
2100 pick: |settings_content| {
2101 settings_content
2102 .editor
2103 .drag_and_drop_selection
2104 .as_ref()
2105 .and_then(|drag_and_drop| drag_and_drop.enabled.as_ref())
2106 },
2107 write: |settings_content, value, _| {
2108 settings_content
2109 .editor
2110 .drag_and_drop_selection
2111 .get_or_insert_default()
2112 .enabled = value;
2113 },
2114 }),
2115 metadata: None,
2116 files: USER,
2117 }),
2118 SettingsPageItem::SettingItem(SettingItem {
2119 title: "Delay",
2120 description: "Delay in milliseconds before drag and drop selection starts.",
2121 field: Box::new(SettingField {
2122 organization_override: None,
2123 json_path: Some("drag_and_drop_selection.delay"),
2124 pick: |settings_content| {
2125 settings_content
2126 .editor
2127 .drag_and_drop_selection
2128 .as_ref()
2129 .and_then(|drag_and_drop| drag_and_drop.delay.as_ref())
2130 },
2131 write: |settings_content, value, _| {
2132 settings_content
2133 .editor
2134 .drag_and_drop_selection
2135 .get_or_insert_default()
2136 .delay = value;
2137 },
2138 }),
2139 metadata: None,
2140 files: USER,
2141 }),
2142 ]
2143 }
2144
2145 fn gutter_section() -> [SettingsPageItem; 9] {
2146 [
2147 SettingsPageItem::SectionHeader("Gutter"),
2148 SettingsPageItem::SettingItem(SettingItem {
2149 title: "Show Line Numbers",
2150 description: "Show line numbers in the gutter.",
2151 field: Box::new(SettingField {
2152 organization_override: None,
2153 json_path: Some("gutter.line_numbers"),
2154 pick: |settings_content| {
2155 settings_content
2156 .editor
2157 .gutter
2158 .as_ref()
2159 .and_then(|gutter| gutter.line_numbers.as_ref())
2160 },
2161 write: |settings_content, value, _| {
2162 settings_content
2163 .editor
2164 .gutter
2165 .get_or_insert_default()
2166 .line_numbers = value;
2167 },
2168 }),
2169 metadata: None,
2170 files: USER,
2171 }),
2172 SettingsPageItem::SettingItem(SettingItem {
2173 title: "Relative Line Numbers",
2174 description: "Controls line number display in the editor's gutter. \"disabled\" shows absolute line numbers, \"enabled\" shows relative line numbers for each absolute line, and \"wrapped\" shows relative line numbers for every line, absolute or wrapped.",
2175 field: Box::new(SettingField {
2176 organization_override: None,
2177 json_path: Some("relative_line_numbers"),
2178 pick: |settings_content| settings_content.editor.relative_line_numbers.as_ref(),
2179 write: |settings_content, value, _| {
2180 settings_content.editor.relative_line_numbers = value;
2181 },
2182 }),
2183 metadata: None,
2184 files: USER,
2185 }),
2186 SettingsPageItem::SettingItem(SettingItem {
2187 title: "Show Runnables",
2188 description: "Show runnable buttons in the gutter.",
2189 field: Box::new(SettingField {
2190 organization_override: None,
2191 json_path: Some("gutter.runnables"),
2192 pick: |settings_content| {
2193 settings_content
2194 .editor
2195 .gutter
2196 .as_ref()
2197 .and_then(|gutter| gutter.runnables.as_ref())
2198 },
2199 write: |settings_content, value, _| {
2200 settings_content
2201 .editor
2202 .gutter
2203 .get_or_insert_default()
2204 .runnables = value;
2205 },
2206 }),
2207 metadata: None,
2208 files: USER,
2209 }),
2210 SettingsPageItem::SettingItem(SettingItem {
2211 title: "Show Breakpoints",
2212 description: "Show breakpoints in the gutter.",
2213 field: Box::new(SettingField {
2214 organization_override: None,
2215 json_path: Some("gutter.breakpoints"),
2216 pick: |settings_content| {
2217 settings_content
2218 .editor
2219 .gutter
2220 .as_ref()
2221 .and_then(|gutter| gutter.breakpoints.as_ref())
2222 },
2223 write: |settings_content, value, _| {
2224 settings_content
2225 .editor
2226 .gutter
2227 .get_or_insert_default()
2228 .breakpoints = value;
2229 },
2230 }),
2231 metadata: None,
2232 files: USER,
2233 }),
2234 SettingsPageItem::SettingItem(SettingItem {
2235 title: "Show Bookmarks",
2236 description: "Show bookmarks in the gutter.",
2237 field: Box::new(SettingField {
2238 organization_override: None,
2239 json_path: Some("gutter.bookmarks"),
2240 pick: |settings_content| {
2241 settings_content
2242 .editor
2243 .gutter
2244 .as_ref()
2245 .and_then(|gutter| gutter.bookmarks.as_ref())
2246 },
2247 write: |settings_content, value, _| {
2248 settings_content
2249 .editor
2250 .gutter
2251 .get_or_insert_default()
2252 .bookmarks = value;
2253 },
2254 }),
2255 metadata: None,
2256 files: USER,
2257 }),
2258 SettingsPageItem::SettingItem(SettingItem {
2259 title: "Show Folds",
2260 description: "Show code folding controls in the gutter.",
2261 field: Box::new(SettingField {
2262 organization_override: None,
2263 json_path: Some("gutter.folds"),
2264 pick: |settings_content| {
2265 settings_content
2266 .editor
2267 .gutter
2268 .as_ref()
2269 .and_then(|gutter| gutter.folds.as_ref())
2270 },
2271 write: |settings_content, value, _| {
2272 settings_content.editor.gutter.get_or_insert_default().folds = value;
2273 },
2274 }),
2275 metadata: None,
2276 files: USER,
2277 }),
2278 SettingsPageItem::SettingItem(SettingItem {
2279 title: "Min Line Number Digits",
2280 description: "Minimum number of characters to reserve space for in the gutter.",
2281 field: Box::new(SettingField {
2282 organization_override: None,
2283 json_path: Some("gutter.min_line_number_digits"),
2284 pick: |settings_content| {
2285 settings_content
2286 .editor
2287 .gutter
2288 .as_ref()
2289 .and_then(|gutter| gutter.min_line_number_digits.as_ref())
2290 },
2291 write: |settings_content, value, _| {
2292 settings_content
2293 .editor
2294 .gutter
2295 .get_or_insert_default()
2296 .min_line_number_digits = value;
2297 },
2298 }),
2299 metadata: None,
2300 files: USER,
2301 }),
2302 SettingsPageItem::SettingItem(SettingItem {
2303 title: "Inline Code Actions",
2304 description: "Show code action button at start of buffer line.",
2305 field: Box::new(SettingField {
2306 organization_override: None,
2307 json_path: Some("inline_code_actions"),
2308 pick: |settings_content| settings_content.editor.inline_code_actions.as_ref(),
2309 write: |settings_content, value, _| {
2310 settings_content.editor.inline_code_actions = value;
2311 },
2312 }),
2313 metadata: None,
2314 files: USER,
2315 }),
2316 ]
2317 }
2318
2319 fn scrollbar_section() -> [SettingsPageItem; 10] {
2320 [
2321 SettingsPageItem::SectionHeader("Scrollbar"),
2322 SettingsPageItem::SettingItem(SettingItem {
2323 title: "Show",
2324 description: "When to show the scrollbar in the editor.",
2325 field: Box::new(SettingField {
2326 organization_override: None,
2327 json_path: Some("scrollbar"),
2328 pick: |settings_content| {
2329 settings_content.editor.scrollbar.as_ref()?.show.as_ref()
2330 },
2331 write: |settings_content, value, _| {
2332 settings_content
2333 .editor
2334 .scrollbar
2335 .get_or_insert_default()
2336 .show = value;
2337 },
2338 }),
2339 metadata: None,
2340 files: USER,
2341 }),
2342 SettingsPageItem::SettingItem(SettingItem {
2343 title: "Cursors",
2344 description: "Show cursor positions in the scrollbar.",
2345 field: Box::new(SettingField {
2346 organization_override: None,
2347 json_path: Some("scrollbar.cursors"),
2348 pick: |settings_content| {
2349 settings_content.editor.scrollbar.as_ref()?.cursors.as_ref()
2350 },
2351 write: |settings_content, value, _| {
2352 settings_content
2353 .editor
2354 .scrollbar
2355 .get_or_insert_default()
2356 .cursors = value;
2357 },
2358 }),
2359 metadata: None,
2360 files: USER,
2361 }),
2362 SettingsPageItem::SettingItem(SettingItem {
2363 title: "Git Diff",
2364 description: "Show Git diff indicators in the scrollbar.",
2365 field: Box::new(SettingField {
2366 organization_override: None,
2367 json_path: Some("scrollbar.git_diff"),
2368 pick: |settings_content| {
2369 settings_content
2370 .editor
2371 .scrollbar
2372 .as_ref()?
2373 .git_diff
2374 .as_ref()
2375 },
2376 write: |settings_content, value, _| {
2377 settings_content
2378 .editor
2379 .scrollbar
2380 .get_or_insert_default()
2381 .git_diff = value;
2382 },
2383 }),
2384 metadata: None,
2385 files: USER,
2386 }),
2387 SettingsPageItem::SettingItem(SettingItem {
2388 title: "Search Results",
2389 description: "Show buffer search result indicators in the scrollbar.",
2390 field: Box::new(SettingField {
2391 organization_override: None,
2392 json_path: Some("scrollbar.search_results"),
2393 pick: |settings_content| {
2394 settings_content
2395 .editor
2396 .scrollbar
2397 .as_ref()?
2398 .search_results
2399 .as_ref()
2400 },
2401 write: |settings_content, value, _| {
2402 settings_content
2403 .editor
2404 .scrollbar
2405 .get_or_insert_default()
2406 .search_results = value;
2407 },
2408 }),
2409 metadata: None,
2410 files: USER,
2411 }),
2412 SettingsPageItem::SettingItem(SettingItem {
2413 title: "Selected Text",
2414 description: "Show selected text occurrences in the scrollbar.",
2415 field: Box::new(SettingField {
2416 organization_override: None,
2417 json_path: Some("scrollbar.selected_text"),
2418 pick: |settings_content| {
2419 settings_content
2420 .editor
2421 .scrollbar
2422 .as_ref()?
2423 .selected_text
2424 .as_ref()
2425 },
2426 write: |settings_content, value, _| {
2427 settings_content
2428 .editor
2429 .scrollbar
2430 .get_or_insert_default()
2431 .selected_text = value;
2432 },
2433 }),
2434 metadata: None,
2435 files: USER,
2436 }),
2437 SettingsPageItem::SettingItem(SettingItem {
2438 title: "Selected Symbol",
2439 description: "Show selected symbol occurrences in the scrollbar.",
2440 field: Box::new(SettingField {
2441 organization_override: None,
2442 json_path: Some("scrollbar.selected_symbol"),
2443 pick: |settings_content| {
2444 settings_content
2445 .editor
2446 .scrollbar
2447 .as_ref()?
2448 .selected_symbol
2449 .as_ref()
2450 },
2451 write: |settings_content, value, _| {
2452 settings_content
2453 .editor
2454 .scrollbar
2455 .get_or_insert_default()
2456 .selected_symbol = value;
2457 },
2458 }),
2459 metadata: None,
2460 files: USER,
2461 }),
2462 SettingsPageItem::SettingItem(SettingItem {
2463 title: "Diagnostics",
2464 description: "Which diagnostic indicators to show in the scrollbar.",
2465 field: Box::new(SettingField {
2466 organization_override: None,
2467 json_path: Some("scrollbar.diagnostics"),
2468 pick: |settings_content| {
2469 settings_content
2470 .editor
2471 .scrollbar
2472 .as_ref()?
2473 .diagnostics
2474 .as_ref()
2475 },
2476 write: |settings_content, value, _| {
2477 settings_content
2478 .editor
2479 .scrollbar
2480 .get_or_insert_default()
2481 .diagnostics = value;
2482 },
2483 }),
2484 metadata: None,
2485 files: USER,
2486 }),
2487 SettingsPageItem::SettingItem(SettingItem {
2488 title: "Horizontal Scrollbar",
2489 description: "When false, forcefully disables the horizontal scrollbar.",
2490 field: Box::new(SettingField {
2491 organization_override: None,
2492 json_path: Some("scrollbar.axes.horizontal"),
2493 pick: |settings_content| {
2494 settings_content
2495 .editor
2496 .scrollbar
2497 .as_ref()?
2498 .axes
2499 .as_ref()?
2500 .horizontal
2501 .as_ref()
2502 },
2503 write: |settings_content, value, _| {
2504 settings_content
2505 .editor
2506 .scrollbar
2507 .get_or_insert_default()
2508 .axes
2509 .get_or_insert_default()
2510 .horizontal = value;
2511 },
2512 }),
2513 metadata: None,
2514 files: USER,
2515 }),
2516 SettingsPageItem::SettingItem(SettingItem {
2517 title: "Vertical Scrollbar",
2518 description: "When false, forcefully disables the vertical scrollbar.",
2519 field: Box::new(SettingField {
2520 organization_override: None,
2521 json_path: Some("scrollbar.axes.vertical"),
2522 pick: |settings_content| {
2523 settings_content
2524 .editor
2525 .scrollbar
2526 .as_ref()?
2527 .axes
2528 .as_ref()?
2529 .vertical
2530 .as_ref()
2531 },
2532 write: |settings_content, value, _| {
2533 settings_content
2534 .editor
2535 .scrollbar
2536 .get_or_insert_default()
2537 .axes
2538 .get_or_insert_default()
2539 .vertical = value;
2540 },
2541 }),
2542 metadata: None,
2543 files: USER,
2544 }),
2545 ]
2546 }
2547
2548 fn minimap_section() -> [SettingsPageItem; 7] {
2549 [
2550 SettingsPageItem::SectionHeader("Minimap"),
2551 SettingsPageItem::SettingItem(SettingItem {
2552 title: "Show",
2553 description: "When to show the minimap in the editor.",
2554 field: Box::new(SettingField {
2555 organization_override: None,
2556 json_path: Some("minimap.show"),
2557 pick: |settings_content| {
2558 settings_content.editor.minimap.as_ref()?.show.as_ref()
2559 },
2560 write: |settings_content, value, _| {
2561 settings_content.editor.minimap.get_or_insert_default().show = value;
2562 },
2563 }),
2564 metadata: None,
2565 files: USER,
2566 }),
2567 SettingsPageItem::SettingItem(SettingItem {
2568 title: "Display In",
2569 description: "Where to show the minimap in the editor.",
2570 field: Box::new(SettingField {
2571 organization_override: None,
2572 json_path: Some("minimap.display_in"),
2573 pick: |settings_content| {
2574 settings_content
2575 .editor
2576 .minimap
2577 .as_ref()?
2578 .display_in
2579 .as_ref()
2580 },
2581 write: |settings_content, value, _| {
2582 settings_content
2583 .editor
2584 .minimap
2585 .get_or_insert_default()
2586 .display_in = value;
2587 },
2588 }),
2589 metadata: None,
2590 files: USER,
2591 }),
2592 SettingsPageItem::SettingItem(SettingItem {
2593 title: "Thumb",
2594 description: "When to show the minimap thumb.",
2595 field: Box::new(SettingField {
2596 organization_override: None,
2597 json_path: Some("minimap.thumb"),
2598 pick: |settings_content| {
2599 settings_content.editor.minimap.as_ref()?.thumb.as_ref()
2600 },
2601 write: |settings_content, value, _| {
2602 settings_content
2603 .editor
2604 .minimap
2605 .get_or_insert_default()
2606 .thumb = value;
2607 },
2608 }),
2609 metadata: None,
2610 files: USER,
2611 }),
2612 SettingsPageItem::SettingItem(SettingItem {
2613 title: "Thumb Border",
2614 description: "Border style for the minimap's scrollbar thumb.",
2615 field: Box::new(SettingField {
2616 organization_override: None,
2617 json_path: Some("minimap.thumb_border"),
2618 pick: |settings_content| {
2619 settings_content
2620 .editor
2621 .minimap
2622 .as_ref()?
2623 .thumb_border
2624 .as_ref()
2625 },
2626 write: |settings_content, value, _| {
2627 settings_content
2628 .editor
2629 .minimap
2630 .get_or_insert_default()
2631 .thumb_border = value;
2632 },
2633 }),
2634 metadata: None,
2635 files: USER,
2636 }),
2637 SettingsPageItem::SettingItem(SettingItem {
2638 title: "Current Line Highlight",
2639 description: "How to highlight the current line in the minimap.",
2640 field: Box::new(SettingField {
2641 organization_override: None,
2642 json_path: Some("minimap.current_line_highlight"),
2643 pick: |settings_content| {
2644 settings_content
2645 .editor
2646 .minimap
2647 .as_ref()
2648 .and_then(|minimap| minimap.current_line_highlight.as_ref())
2649 .or(settings_content.editor.current_line_highlight.as_ref())
2650 },
2651 write: |settings_content, value, _| {
2652 settings_content
2653 .editor
2654 .minimap
2655 .get_or_insert_default()
2656 .current_line_highlight = value;
2657 },
2658 }),
2659 metadata: None,
2660 files: USER,
2661 }),
2662 SettingsPageItem::SettingItem(SettingItem {
2663 title: "Max Width Columns",
2664 description: "Maximum number of columns to display in the minimap.",
2665 field: Box::new(SettingField {
2666 organization_override: None,
2667 json_path: Some("minimap.max_width_columns"),
2668 pick: |settings_content| {
2669 settings_content
2670 .editor
2671 .minimap
2672 .as_ref()?
2673 .max_width_columns
2674 .as_ref()
2675 },
2676 write: |settings_content, value, _| {
2677 settings_content
2678 .editor
2679 .minimap
2680 .get_or_insert_default()
2681 .max_width_columns = value;
2682 },
2683 }),
2684 metadata: None,
2685 files: USER,
2686 }),
2687 ]
2688 }
2689
2690 fn toolbar_section() -> [SettingsPageItem; 6] {
2691 [
2692 SettingsPageItem::SectionHeader("Toolbar"),
2693 SettingsPageItem::SettingItem(SettingItem {
2694 title: "Breadcrumbs",
2695 description: "Show breadcrumbs.",
2696 field: Box::new(SettingField {
2697 organization_override: None,
2698 json_path: Some("toolbar.breadcrumbs"),
2699 pick: |settings_content| {
2700 settings_content
2701 .editor
2702 .toolbar
2703 .as_ref()?
2704 .breadcrumbs
2705 .as_ref()
2706 },
2707 write: |settings_content, value, _| {
2708 settings_content
2709 .editor
2710 .toolbar
2711 .get_or_insert_default()
2712 .breadcrumbs = value;
2713 },
2714 }),
2715 metadata: None,
2716 files: USER,
2717 }),
2718 SettingsPageItem::SettingItem(SettingItem {
2719 title: "Quick Actions",
2720 description: "Show quick action buttons (e.g., search, selection, editor controls, etc.).",
2721 field: Box::new(SettingField {
2722 organization_override: None,
2723 json_path: Some("toolbar.quick_actions"),
2724 pick: |settings_content| {
2725 settings_content
2726 .editor
2727 .toolbar
2728 .as_ref()?
2729 .quick_actions
2730 .as_ref()
2731 },
2732 write: |settings_content, value, _| {
2733 settings_content
2734 .editor
2735 .toolbar
2736 .get_or_insert_default()
2737 .quick_actions = value;
2738 },
2739 }),
2740 metadata: None,
2741 files: USER,
2742 }),
2743 SettingsPageItem::SettingItem(SettingItem {
2744 title: "Selections Menu",
2745 description: "Show the selections menu in the editor toolbar.",
2746 field: Box::new(SettingField {
2747 organization_override: None,
2748 json_path: Some("toolbar.selections_menu"),
2749 pick: |settings_content| {
2750 settings_content
2751 .editor
2752 .toolbar
2753 .as_ref()?
2754 .selections_menu
2755 .as_ref()
2756 },
2757 write: |settings_content, value, _| {
2758 settings_content
2759 .editor
2760 .toolbar
2761 .get_or_insert_default()
2762 .selections_menu = value;
2763 },
2764 }),
2765 metadata: None,
2766 files: USER,
2767 }),
2768 SettingsPageItem::SettingItem(SettingItem {
2769 title: "Agent Review",
2770 description: "Show agent review buttons in the editor toolbar.",
2771 field: Box::new(SettingField {
2772 organization_override: None,
2773 json_path: Some("toolbar.agent_review"),
2774 pick: |settings_content| {
2775 settings_content
2776 .editor
2777 .toolbar
2778 .as_ref()?
2779 .agent_review
2780 .as_ref()
2781 },
2782 write: |settings_content, value, _| {
2783 settings_content
2784 .editor
2785 .toolbar
2786 .get_or_insert_default()
2787 .agent_review = value;
2788 },
2789 }),
2790 metadata: None,
2791 files: USER,
2792 }),
2793 SettingsPageItem::SettingItem(SettingItem {
2794 title: "Code Actions",
2795 description: "Show code action buttons in the editor toolbar.",
2796 field: Box::new(SettingField {
2797 organization_override: None,
2798 json_path: Some("toolbar.code_actions"),
2799 pick: |settings_content| {
2800 settings_content
2801 .editor
2802 .toolbar
2803 .as_ref()?
2804 .code_actions
2805 .as_ref()
2806 },
2807 write: |settings_content, value, _| {
2808 settings_content
2809 .editor
2810 .toolbar
2811 .get_or_insert_default()
2812 .code_actions = value;
2813 },
2814 }),
2815 metadata: None,
2816 files: USER,
2817 }),
2818 ]
2819 }
2820
2821 fn vim_settings_section() -> [SettingsPageItem; 14] {
2822 [
2823 SettingsPageItem::SectionHeader("Vim"),
2824 SettingsPageItem::SettingItem(SettingItem {
2825 title: "Default Mode",
2826 description: "The default mode when Vim starts.",
2827 field: Box::new(SettingField {
2828 organization_override: None,
2829 json_path: Some("vim.default_mode"),
2830 pick: |settings_content| settings_content.vim.as_ref()?.default_mode.as_ref(),
2831 write: |settings_content, value, _| {
2832 settings_content.vim.get_or_insert_default().default_mode = value;
2833 },
2834 }),
2835 metadata: None,
2836 files: USER,
2837 }),
2838 SettingsPageItem::SettingItem(SettingItem {
2839 title: "Toggle Relative Line Numbers",
2840 description: "Toggle relative line numbers in Vim mode.",
2841 field: Box::new(SettingField {
2842 organization_override: None,
2843 json_path: Some("vim.toggle_relative_line_numbers"),
2844 pick: |settings_content| {
2845 settings_content
2846 .vim
2847 .as_ref()?
2848 .toggle_relative_line_numbers
2849 .as_ref()
2850 },
2851 write: |settings_content, value, _| {
2852 settings_content
2853 .vim
2854 .get_or_insert_default()
2855 .toggle_relative_line_numbers = value;
2856 },
2857 }),
2858 metadata: None,
2859 files: USER,
2860 }),
2861 SettingsPageItem::SettingItem(SettingItem {
2862 title: "Use System Clipboard",
2863 description: "Controls when to use system clipboard in Vim mode.",
2864 field: Box::new(SettingField {
2865 organization_override: None,
2866 json_path: Some("vim.use_system_clipboard"),
2867 pick: |settings_content| {
2868 settings_content.vim.as_ref()?.use_system_clipboard.as_ref()
2869 },
2870 write: |settings_content, value, _| {
2871 settings_content
2872 .vim
2873 .get_or_insert_default()
2874 .use_system_clipboard = value;
2875 },
2876 }),
2877 metadata: None,
2878 files: USER,
2879 }),
2880 SettingsPageItem::SettingItem(SettingItem {
2881 title: "Use Smartcase Find",
2882 description: "Enable smartcase searching in Vim mode.",
2883 field: Box::new(SettingField {
2884 organization_override: None,
2885 json_path: Some("vim.use_smartcase_find"),
2886 pick: |settings_content| {
2887 settings_content.vim.as_ref()?.use_smartcase_find.as_ref()
2888 },
2889 write: |settings_content, value, _| {
2890 settings_content
2891 .vim
2892 .get_or_insert_default()
2893 .use_smartcase_find = value;
2894 },
2895 }),
2896 metadata: None,
2897 files: USER,
2898 }),
2899 SettingsPageItem::SettingItem(SettingItem {
2900 title: "Global Substitution Default",
2901 description: "When enabled, the :substitute command replaces all matches in a line by default. The 'g' flag then toggles this behavior.",
2902 field: Box::new(SettingField {
2903 organization_override: None,
2904 json_path: Some("vim.gdefault"),
2905 pick: |settings_content| settings_content.vim.as_ref()?.gdefault.as_ref(),
2906 write: |settings_content, value, _| {
2907 settings_content.vim.get_or_insert_default().gdefault = value;
2908 },
2909 }),
2910 metadata: None,
2911 files: USER,
2912 }),
2913 SettingsPageItem::SettingItem(SettingItem {
2914 title: "Highlight on Yank Duration",
2915 description: "Duration in milliseconds to highlight yanked text in Vim mode.",
2916 field: Box::new(SettingField {
2917 organization_override: None,
2918 json_path: Some("vim.highlight_on_yank_duration"),
2919 pick: |settings_content| {
2920 settings_content
2921 .vim
2922 .as_ref()?
2923 .highlight_on_yank_duration
2924 .as_ref()
2925 },
2926 write: |settings_content, value, _| {
2927 settings_content
2928 .vim
2929 .get_or_insert_default()
2930 .highlight_on_yank_duration = value;
2931 },
2932 }),
2933 metadata: None,
2934 files: USER,
2935 }),
2936 SettingsPageItem::SettingItem(SettingItem {
2937 title: "Regex Search",
2938 description: "Use regex search by default in Vim search.",
2939 field: Box::new(SettingField {
2940 organization_override: None,
2941 json_path: Some("vim.use_regex_search"),
2942 pick: |settings_content| {
2943 settings_content.vim.as_ref()?.use_regex_search.as_ref()
2944 },
2945 write: |settings_content, value, _| {
2946 settings_content
2947 .vim
2948 .get_or_insert_default()
2949 .use_regex_search = value;
2950 },
2951 }),
2952 metadata: None,
2953 files: USER,
2954 }),
2955 SettingsPageItem::SettingItem(SettingItem {
2956 title: "Show Edit Predictions in Normal Mode",
2957 description: "Whether edit predictions are shown in normal mode. By default, edit predictions are only shown in insert and replace modes.",
2958 field: Box::new(SettingField {
2959 organization_override: None,
2960 json_path: Some("vim.show_edit_predictions_in_normal_mode"),
2961 pick: |settings_content| {
2962 settings_content
2963 .vim
2964 .as_ref()?
2965 .show_edit_predictions_in_normal_mode
2966 .as_ref()
2967 },
2968 write: |settings_content, value, _| {
2969 settings_content
2970 .vim
2971 .get_or_insert_default()
2972 .show_edit_predictions_in_normal_mode = value;
2973 },
2974 }),
2975 metadata: None,
2976 files: USER,
2977 }),
2978 SettingsPageItem::SettingItem(SettingItem {
2979 title: "Cursor Shape - Normal Mode",
2980 description: "Cursor shape for normal mode.",
2981 field: Box::new(SettingField {
2982 organization_override: None,
2983 json_path: Some("vim.cursor_shape.normal"),
2984 pick: |settings_content| {
2985 settings_content
2986 .vim
2987 .as_ref()?
2988 .cursor_shape
2989 .as_ref()?
2990 .normal
2991 .as_ref()
2992 },
2993 write: |settings_content, value, _| {
2994 settings_content
2995 .vim
2996 .get_or_insert_default()
2997 .cursor_shape
2998 .get_or_insert_default()
2999 .normal = value;
3000 },
3001 }),
3002 metadata: None,
3003 files: USER,
3004 }),
3005 SettingsPageItem::SettingItem(SettingItem {
3006 title: "Cursor Shape - Insert Mode",
3007 description: "Cursor shape for insert mode. Inherit uses the editor's cursor shape.",
3008 field: Box::new(SettingField {
3009 organization_override: None,
3010 json_path: Some("vim.cursor_shape.insert"),
3011 pick: |settings_content| {
3012 settings_content
3013 .vim
3014 .as_ref()?
3015 .cursor_shape
3016 .as_ref()?
3017 .insert
3018 .as_ref()
3019 },
3020 write: |settings_content, value, _| {
3021 settings_content
3022 .vim
3023 .get_or_insert_default()
3024 .cursor_shape
3025 .get_or_insert_default()
3026 .insert = value;
3027 },
3028 }),
3029 metadata: None,
3030 files: USER,
3031 }),
3032 SettingsPageItem::SettingItem(SettingItem {
3033 title: "Cursor Shape - Replace Mode",
3034 description: "Cursor shape for replace mode.",
3035 field: Box::new(SettingField {
3036 organization_override: None,
3037 json_path: Some("vim.cursor_shape.replace"),
3038 pick: |settings_content| {
3039 settings_content
3040 .vim
3041 .as_ref()?
3042 .cursor_shape
3043 .as_ref()?
3044 .replace
3045 .as_ref()
3046 },
3047 write: |settings_content, value, _| {
3048 settings_content
3049 .vim
3050 .get_or_insert_default()
3051 .cursor_shape
3052 .get_or_insert_default()
3053 .replace = value;
3054 },
3055 }),
3056 metadata: None,
3057 files: USER,
3058 }),
3059 SettingsPageItem::SettingItem(SettingItem {
3060 title: "Cursor Shape - Visual Mode",
3061 description: "Cursor shape for visual mode.",
3062 field: Box::new(SettingField {
3063 organization_override: None,
3064 json_path: Some("vim.cursor_shape.visual"),
3065 pick: |settings_content| {
3066 settings_content
3067 .vim
3068 .as_ref()?
3069 .cursor_shape
3070 .as_ref()?
3071 .visual
3072 .as_ref()
3073 },
3074 write: |settings_content, value, _| {
3075 settings_content
3076 .vim
3077 .get_or_insert_default()
3078 .cursor_shape
3079 .get_or_insert_default()
3080 .visual = value;
3081 },
3082 }),
3083 metadata: None,
3084 files: USER,
3085 }),
3086 SettingsPageItem::SettingItem(SettingItem {
3087 title: "Custom Digraphs",
3088 description: "Custom digraph mappings for Vim mode.",
3089 field: Box::new(
3090 SettingField {
3091 organization_override: None,
3092 json_path: Some("vim.custom_digraphs"),
3093 pick: |settings_content| {
3094 settings_content.vim.as_ref()?.custom_digraphs.as_ref()
3095 },
3096 write: |settings_content, value, _| {
3097 settings_content.vim.get_or_insert_default().custom_digraphs = value;
3098 },
3099 }
3100 .unimplemented(),
3101 ),
3102 metadata: None,
3103 files: USER,
3104 }),
3105 ]
3106 }
3107
3108 let items = concat_sections!(
3109 auto_save_section(),
3110 which_key_section(),
3111 multibuffer_section(),
3112 scrolling_section(),
3113 signature_help_section(),
3114 hover_popover_section(),
3115 drag_and_drop_selection_section(),
3116 gutter_section(),
3117 scrollbar_section(),
3118 minimap_section(),
3119 toolbar_section(),
3120 vim_settings_section(),
3121 language_settings_data(),
3122 );
3123
3124 SettingsPage {
3125 title: "Editor",
3126 items: items,
3127 }
3128}
3129
3130fn languages_and_tools_page(cx: &App) -> SettingsPage {
3131 fn file_types_section() -> [SettingsPageItem; 2] {
3132 [
3133 SettingsPageItem::SectionHeader("File Types"),
3134 SettingsPageItem::SettingItem(SettingItem {
3135 title: "File Type Associations",
3136 description: "A mapping from languages to files and file extensions that should be treated as that language.",
3137 field: Box::new(
3138 SettingField {
3139 organization_override: None,
3140 json_path: Some("file_type_associations"),
3141 pick: |settings_content| {
3142 settings_content.project.all_languages.file_types.as_ref()
3143 },
3144 write: |settings_content, value, _| {
3145 settings_content.project.all_languages.file_types = value;
3146 },
3147 }
3148 .unimplemented(),
3149 ),
3150 metadata: None,
3151 files: USER | PROJECT,
3152 }),
3153 ]
3154 }
3155
3156 fn diagnostics_section() -> [SettingsPageItem; 3] {
3157 [
3158 SettingsPageItem::SectionHeader("Diagnostics"),
3159 SettingsPageItem::SettingItem(SettingItem {
3160 title: "Max Severity",
3161 description: "Which level to use to filter out diagnostics displayed in the editor.",
3162 field: Box::new(SettingField {
3163 organization_override: None,
3164 json_path: Some("diagnostics_max_severity"),
3165 pick: |settings_content| {
3166 settings_content.editor.diagnostics_max_severity.as_ref()
3167 },
3168 write: |settings_content, value, _| {
3169 settings_content.editor.diagnostics_max_severity = value;
3170 },
3171 }),
3172 metadata: None,
3173 files: USER,
3174 }),
3175 SettingsPageItem::SettingItem(SettingItem {
3176 title: "Include Warnings",
3177 description: "Whether to show warnings or not by default.",
3178 field: Box::new(SettingField {
3179 organization_override: None,
3180 json_path: Some("diagnostics.include_warnings"),
3181 pick: |settings_content| {
3182 settings_content
3183 .diagnostics
3184 .as_ref()?
3185 .include_warnings
3186 .as_ref()
3187 },
3188 write: |settings_content, value, _| {
3189 settings_content
3190 .diagnostics
3191 .get_or_insert_default()
3192 .include_warnings = value;
3193 },
3194 }),
3195 metadata: None,
3196 files: USER,
3197 }),
3198 ]
3199 }
3200
3201 fn inline_diagnostics_section() -> [SettingsPageItem; 5] {
3202 [
3203 SettingsPageItem::SectionHeader("Inline Diagnostics"),
3204 SettingsPageItem::SettingItem(SettingItem {
3205 title: "Enabled",
3206 description: "Whether to show diagnostics inline or not.",
3207 field: Box::new(SettingField {
3208 organization_override: None,
3209 json_path: Some("diagnostics.inline.enabled"),
3210 pick: |settings_content| {
3211 settings_content
3212 .diagnostics
3213 .as_ref()?
3214 .inline
3215 .as_ref()?
3216 .enabled
3217 .as_ref()
3218 },
3219 write: |settings_content, value, _| {
3220 settings_content
3221 .diagnostics
3222 .get_or_insert_default()
3223 .inline
3224 .get_or_insert_default()
3225 .enabled = value;
3226 },
3227 }),
3228 metadata: None,
3229 files: USER,
3230 }),
3231 SettingsPageItem::SettingItem(SettingItem {
3232 title: "Update Debounce",
3233 description: "The delay in milliseconds to show inline diagnostics after the last diagnostic update.",
3234 field: Box::new(SettingField {
3235 organization_override: None,
3236 json_path: Some("diagnostics.inline.update_debounce_ms"),
3237 pick: |settings_content| {
3238 settings_content
3239 .diagnostics
3240 .as_ref()?
3241 .inline
3242 .as_ref()?
3243 .update_debounce_ms
3244 .as_ref()
3245 },
3246 write: |settings_content, value, _| {
3247 settings_content
3248 .diagnostics
3249 .get_or_insert_default()
3250 .inline
3251 .get_or_insert_default()
3252 .update_debounce_ms = value;
3253 },
3254 }),
3255 metadata: None,
3256 files: USER,
3257 }),
3258 SettingsPageItem::SettingItem(SettingItem {
3259 title: "Padding",
3260 description: "The amount of padding between the end of the source line and the start of the inline diagnostic.",
3261 field: Box::new(SettingField {
3262 organization_override: None,
3263 json_path: Some("diagnostics.inline.padding"),
3264 pick: |settings_content| {
3265 settings_content
3266 .diagnostics
3267 .as_ref()?
3268 .inline
3269 .as_ref()?
3270 .padding
3271 .as_ref()
3272 },
3273 write: |settings_content, value, _| {
3274 settings_content
3275 .diagnostics
3276 .get_or_insert_default()
3277 .inline
3278 .get_or_insert_default()
3279 .padding = value;
3280 },
3281 }),
3282 metadata: None,
3283 files: USER,
3284 }),
3285 SettingsPageItem::SettingItem(SettingItem {
3286 title: "Minimum Column",
3287 description: "The minimum column at which to display inline diagnostics.",
3288 field: Box::new(SettingField {
3289 organization_override: None,
3290 json_path: Some("diagnostics.inline.min_column"),
3291 pick: |settings_content| {
3292 settings_content
3293 .diagnostics
3294 .as_ref()?
3295 .inline
3296 .as_ref()?
3297 .min_column
3298 .as_ref()
3299 },
3300 write: |settings_content, value, _| {
3301 settings_content
3302 .diagnostics
3303 .get_or_insert_default()
3304 .inline
3305 .get_or_insert_default()
3306 .min_column = value;
3307 },
3308 }),
3309 metadata: None,
3310 files: USER,
3311 }),
3312 ]
3313 }
3314
3315 fn lsp_pull_diagnostics_section() -> [SettingsPageItem; 3] {
3316 [
3317 SettingsPageItem::SectionHeader("LSP Pull Diagnostics"),
3318 SettingsPageItem::SettingItem(SettingItem {
3319 title: "Enabled",
3320 description: "Whether to pull for language server-powered diagnostics or not.",
3321 field: Box::new(SettingField {
3322 organization_override: None,
3323 json_path: Some("diagnostics.lsp_pull_diagnostics.enabled"),
3324 pick: |settings_content| {
3325 settings_content
3326 .diagnostics
3327 .as_ref()?
3328 .lsp_pull_diagnostics
3329 .as_ref()?
3330 .enabled
3331 .as_ref()
3332 },
3333 write: |settings_content, value, _| {
3334 settings_content
3335 .diagnostics
3336 .get_or_insert_default()
3337 .lsp_pull_diagnostics
3338 .get_or_insert_default()
3339 .enabled = value;
3340 },
3341 }),
3342 metadata: None,
3343 files: USER,
3344 }),
3345 // todo(settings_ui): Needs unit
3346 SettingsPageItem::SettingItem(SettingItem {
3347 title: "Debounce",
3348 description: "Minimum time to wait before pulling diagnostics from the language server(s).",
3349 field: Box::new(SettingField {
3350 organization_override: None,
3351 json_path: Some("diagnostics.lsp_pull_diagnostics.debounce_ms"),
3352 pick: |settings_content| {
3353 settings_content
3354 .diagnostics
3355 .as_ref()?
3356 .lsp_pull_diagnostics
3357 .as_ref()?
3358 .debounce_ms
3359 .as_ref()
3360 },
3361 write: |settings_content, value, _| {
3362 settings_content
3363 .diagnostics
3364 .get_or_insert_default()
3365 .lsp_pull_diagnostics
3366 .get_or_insert_default()
3367 .debounce_ms = value;
3368 },
3369 }),
3370 metadata: None,
3371 files: USER,
3372 }),
3373 ]
3374 }
3375
3376 fn lsp_highlights_section() -> [SettingsPageItem; 2] {
3377 [
3378 SettingsPageItem::SectionHeader("LSP Highlights"),
3379 SettingsPageItem::SettingItem(SettingItem {
3380 title: "Debounce",
3381 description: "The debounce delay before querying highlights from the language.",
3382 field: Box::new(SettingField {
3383 organization_override: None,
3384 json_path: Some("lsp_highlight_debounce"),
3385 pick: |settings_content| {
3386 settings_content.editor.lsp_highlight_debounce.as_ref()
3387 },
3388 write: |settings_content, value, _| {
3389 settings_content.editor.lsp_highlight_debounce = value;
3390 },
3391 }),
3392 metadata: None,
3393 files: USER,
3394 }),
3395 ]
3396 }
3397
3398 fn languages_list_section(cx: &App) -> Box<[SettingsPageItem]> {
3399 // todo(settings_ui): Refresh on extension (un)/installed
3400 // Note that `crates/json_schema_store` solves the same problem, there is probably a way to unify the two
3401 std::iter::once(SettingsPageItem::SectionHeader("Languages"))
3402 .chain(all_language_names(cx).into_iter().map(|language_name| {
3403 let link = format!("languages.{language_name}");
3404 SettingsPageItem::SubPageLink(SubPageLink {
3405 title: language_name,
3406 r#type: crate::SubPageType::Language,
3407 description: None,
3408 search_aliases: &[],
3409 json_path: Some(link.leak()),
3410 in_json: true,
3411 files: USER | PROJECT,
3412 render: |this, scroll_handle, window, cx| {
3413 let items: Box<[SettingsPageItem]> = concat_sections!(
3414 language_settings_data(),
3415 non_editor_language_settings_data(),
3416 edit_prediction_language_settings_section()
3417 );
3418 this.render_sub_page_items(
3419 items.iter().enumerate(),
3420 scroll_handle,
3421 window,
3422 cx,
3423 )
3424 .into_any_element()
3425 },
3426 })
3427 }))
3428 .collect()
3429 }
3430
3431 SettingsPage {
3432 title: "Languages & Tools",
3433 items: {
3434 concat_sections!(
3435 non_editor_language_settings_data(),
3436 file_types_section(),
3437 diagnostics_section(),
3438 inline_diagnostics_section(),
3439 lsp_pull_diagnostics_section(),
3440 lsp_highlights_section(),
3441 languages_list_section(cx),
3442 )
3443 },
3444 }
3445}
3446
3447fn search_and_files_page() -> SettingsPage {
3448 fn search_section() -> [SettingsPageItem; 9] {
3449 [
3450 SettingsPageItem::SectionHeader("Search"),
3451 SettingsPageItem::SettingItem(SettingItem {
3452 title: "Whole Word",
3453 description: "Search for whole words by default.",
3454 field: Box::new(SettingField {
3455 organization_override: None,
3456 json_path: Some("search.whole_word"),
3457 pick: |settings_content| {
3458 settings_content.editor.search.as_ref()?.whole_word.as_ref()
3459 },
3460 write: |settings_content, value, _| {
3461 settings_content
3462 .editor
3463 .search
3464 .get_or_insert_default()
3465 .whole_word = value;
3466 },
3467 }),
3468 metadata: None,
3469 files: USER,
3470 }),
3471 SettingsPageItem::SettingItem(SettingItem {
3472 title: "Case Sensitive",
3473 description: "Search case-sensitively by default.",
3474 field: Box::new(SettingField {
3475 organization_override: None,
3476 json_path: Some("search.case_sensitive"),
3477 pick: |settings_content| {
3478 settings_content
3479 .editor
3480 .search
3481 .as_ref()?
3482 .case_sensitive
3483 .as_ref()
3484 },
3485 write: |settings_content, value, _| {
3486 settings_content
3487 .editor
3488 .search
3489 .get_or_insert_default()
3490 .case_sensitive = value;
3491 },
3492 }),
3493 metadata: None,
3494 files: USER,
3495 }),
3496 SettingsPageItem::SettingItem(SettingItem {
3497 title: "Use Smartcase Search",
3498 description: "Whether to automatically enable case-sensitive search based on the search query.",
3499 field: Box::new(SettingField {
3500 organization_override: None,
3501 json_path: Some("use_smartcase_search"),
3502 pick: |settings_content| settings_content.editor.use_smartcase_search.as_ref(),
3503 write: |settings_content, value, _| {
3504 settings_content.editor.use_smartcase_search = value;
3505 },
3506 }),
3507 metadata: None,
3508 files: USER,
3509 }),
3510 SettingsPageItem::SettingItem(SettingItem {
3511 title: "Include Ignored",
3512 description: "Include ignored files in search results by default.",
3513 field: Box::new(SettingField {
3514 organization_override: None,
3515 json_path: Some("search.include_ignored"),
3516 pick: |settings_content| {
3517 settings_content
3518 .editor
3519 .search
3520 .as_ref()?
3521 .include_ignored
3522 .as_ref()
3523 },
3524 write: |settings_content, value, _| {
3525 settings_content
3526 .editor
3527 .search
3528 .get_or_insert_default()
3529 .include_ignored = value;
3530 },
3531 }),
3532 metadata: None,
3533 files: USER,
3534 }),
3535 SettingsPageItem::SettingItem(SettingItem {
3536 title: "Regex",
3537 description: "Use regex search by default.",
3538 field: Box::new(SettingField {
3539 organization_override: None,
3540 json_path: Some("search.regex"),
3541 pick: |settings_content| {
3542 settings_content.editor.search.as_ref()?.regex.as_ref()
3543 },
3544 write: |settings_content, value, _| {
3545 settings_content.editor.search.get_or_insert_default().regex = value;
3546 },
3547 }),
3548 metadata: None,
3549 files: USER,
3550 }),
3551 SettingsPageItem::SettingItem(SettingItem {
3552 title: "Search Wrap",
3553 description: "Whether the editor search results will loop.",
3554 field: Box::new(SettingField {
3555 organization_override: None,
3556 json_path: Some("search_wrap"),
3557 pick: |settings_content| settings_content.editor.search_wrap.as_ref(),
3558 write: |settings_content, value, _| {
3559 settings_content.editor.search_wrap = value;
3560 },
3561 }),
3562 metadata: None,
3563 files: USER,
3564 }),
3565 SettingsPageItem::SettingItem(SettingItem {
3566 title: "Center on Match",
3567 description: "Whether to center the current match in the editor",
3568 field: Box::new(SettingField {
3569 organization_override: None,
3570 json_path: Some("editor.search.center_on_match"),
3571 pick: |settings_content| {
3572 settings_content
3573 .editor
3574 .search
3575 .as_ref()
3576 .and_then(|search| search.center_on_match.as_ref())
3577 },
3578 write: |settings_content, value, _| {
3579 settings_content
3580 .editor
3581 .search
3582 .get_or_insert_default()
3583 .center_on_match = value;
3584 },
3585 }),
3586 metadata: None,
3587 files: USER,
3588 }),
3589 SettingsPageItem::SettingItem(SettingItem {
3590 title: "Seed Search Query From Cursor",
3591 description: "When to populate a new search's query based on the text under the cursor.",
3592 field: Box::new(SettingField {
3593 organization_override: None,
3594 json_path: Some("seed_search_query_from_cursor"),
3595 pick: |settings_content| {
3596 settings_content
3597 .editor
3598 .seed_search_query_from_cursor
3599 .as_ref()
3600 },
3601 write: |settings_content, value, _| {
3602 settings_content.editor.seed_search_query_from_cursor = value;
3603 },
3604 }),
3605 metadata: None,
3606 files: USER,
3607 }),
3608 ]
3609 }
3610
3611 fn file_finder_section() -> [SettingsPageItem; 4] {
3612 [
3613 SettingsPageItem::SectionHeader("File Finder"),
3614 // todo: null by default
3615 SettingsPageItem::SettingItem(SettingItem {
3616 title: "Include Ignored in Search",
3617 description: "Use gitignored files when searching.",
3618 field: Box::new(SettingField {
3619 organization_override: None,
3620 json_path: Some("file_finder.include_ignored"),
3621 pick: |settings_content| {
3622 settings_content
3623 .file_finder
3624 .as_ref()?
3625 .include_ignored
3626 .as_ref()
3627 },
3628 write: |settings_content, value, _| {
3629 settings_content
3630 .file_finder
3631 .get_or_insert_default()
3632 .include_ignored = value;
3633 },
3634 }),
3635 metadata: None,
3636 files: USER,
3637 }),
3638 SettingsPageItem::SettingItem(SettingItem {
3639 title: "File Icons",
3640 description: "Show file icons in the file finder.",
3641 field: Box::new(SettingField {
3642 organization_override: None,
3643 json_path: Some("file_finder.file_icons"),
3644 pick: |settings_content| {
3645 settings_content.file_finder.as_ref()?.file_icons.as_ref()
3646 },
3647 write: |settings_content, value, _| {
3648 settings_content
3649 .file_finder
3650 .get_or_insert_default()
3651 .file_icons = value;
3652 },
3653 }),
3654 metadata: None,
3655 files: USER,
3656 }),
3657 SettingsPageItem::SettingItem(SettingItem {
3658 title: "Skip Focus For Active In Search",
3659 description: "Whether the file finder should skip focus for the active file in search results.",
3660 field: Box::new(SettingField {
3661 organization_override: None,
3662 json_path: Some("file_finder.skip_focus_for_active_in_search"),
3663 pick: |settings_content| {
3664 settings_content
3665 .file_finder
3666 .as_ref()?
3667 .skip_focus_for_active_in_search
3668 .as_ref()
3669 },
3670 write: |settings_content, value, _| {
3671 settings_content
3672 .file_finder
3673 .get_or_insert_default()
3674 .skip_focus_for_active_in_search = value;
3675 },
3676 }),
3677 metadata: None,
3678 files: USER,
3679 }),
3680 ]
3681 }
3682
3683 fn file_scan_section() -> [SettingsPageItem; 6] {
3684 [
3685 SettingsPageItem::SectionHeader("File Scan"),
3686 SettingsPageItem::SettingItem(SettingItem {
3687 title: "File Scan Exclusions",
3688 description: "Files or globs of files that will be excluded by Omega entirely. They will be skipped during file scans, file searches, and not be displayed in the project file tree. Takes precedence over \"File Scan Inclusions\"",
3689 field: Box::new(
3690 SettingField {
3691 organization_override: None,
3692 json_path: Some("file_scan_exclusions"),
3693 pick: |settings_content| {
3694 settings_content
3695 .project
3696 .worktree
3697 .file_scan_exclusions
3698 .as_ref()
3699 },
3700 write: |settings_content, value, _| {
3701 settings_content.project.worktree.file_scan_exclusions = value;
3702 },
3703 }
3704 .unimplemented(),
3705 ),
3706 metadata: None,
3707 files: USER,
3708 }),
3709 SettingsPageItem::SettingItem(SettingItem {
3710 title: "File Scan Inclusions",
3711 description: "Files or globs of files that will be included by Omega, even when ignored by git. This is useful for files that are not tracked by git, but are still important to your project. Note that globs that are overly broad can slow down Omega's file scanning. \"File Scan Exclusions\" takes precedence over these inclusions",
3712 field: Box::new(
3713 SettingField {
3714 organization_override: None,
3715 json_path: Some("file_scan_inclusions"),
3716 pick: |settings_content| {
3717 settings_content
3718 .project
3719 .worktree
3720 .file_scan_inclusions
3721 .as_ref()
3722 },
3723 write: |settings_content, value, _| {
3724 settings_content.project.worktree.file_scan_inclusions = value;
3725 },
3726 }
3727 .unimplemented(),
3728 ),
3729 metadata: None,
3730 files: USER,
3731 }),
3732 SettingsPageItem::SettingItem(SettingItem {
3733 title: "Scan Symbolic Links",
3734 description: "When to scan content of linked directories",
3735 field: Box::new(SettingField {
3736 json_path: Some("scan_symlinks"),
3737 organization_override: None,
3738 pick: |settings_content| {
3739 settings_content.project.worktree.scan_symlinks.as_ref()
3740 },
3741 write: |settings_content, value, _| {
3742 settings_content.project.worktree.scan_symlinks = value;
3743 },
3744 }),
3745 metadata: None,
3746 files: USER,
3747 }),
3748 SettingsPageItem::SettingItem(SettingItem {
3749 title: "Restore File State",
3750 description: "Restore previous file state when reopening.",
3751 field: Box::new(SettingField {
3752 organization_override: None,
3753 json_path: Some("restore_on_file_reopen"),
3754 pick: |settings_content| {
3755 settings_content.workspace.restore_on_file_reopen.as_ref()
3756 },
3757 write: |settings_content, value, _| {
3758 settings_content.workspace.restore_on_file_reopen = value;
3759 },
3760 }),
3761 metadata: None,
3762 files: USER,
3763 }),
3764 SettingsPageItem::SettingItem(SettingItem {
3765 title: "Close on File Delete",
3766 description: "Automatically close files that have been deleted.",
3767 field: Box::new(SettingField {
3768 organization_override: None,
3769 json_path: Some("close_on_file_delete"),
3770 pick: |settings_content| {
3771 settings_content.workspace.close_on_file_delete.as_ref()
3772 },
3773 write: |settings_content, value, _| {
3774 settings_content.workspace.close_on_file_delete = value;
3775 },
3776 }),
3777 metadata: None,
3778 files: USER,
3779 }),
3780 ]
3781 }
3782
3783 SettingsPage {
3784 title: "Search & Files",
3785 items: concat_sections![search_section(), file_finder_section(), file_scan_section()],
3786 }
3787}
3788
3789fn window_and_layout_page() -> SettingsPage {
3790 fn status_bar_section() -> [SettingsPageItem; 11] {
3791 [
3792 SettingsPageItem::SectionHeader("Status Bar"),
3793 SettingsPageItem::SettingItem(SettingItem {
3794 title: "Project Panel Button",
3795 description: "Show the project panel button in the status bar.",
3796 field: Box::new(SettingField {
3797 organization_override: None,
3798 json_path: Some("project_panel.button"),
3799 pick: |settings_content| {
3800 settings_content.project_panel.as_ref()?.button.as_ref()
3801 },
3802 write: |settings_content, value, _| {
3803 settings_content
3804 .project_panel
3805 .get_or_insert_default()
3806 .button = value;
3807 },
3808 }),
3809 metadata: None,
3810 files: USER,
3811 }),
3812 SettingsPageItem::SettingItem(SettingItem {
3813 title: "Active Language Button",
3814 description: "Show the active language button in the status bar.",
3815 field: Box::new(SettingField {
3816 organization_override: None,
3817 json_path: Some("status_bar.active_language_button"),
3818 pick: |settings_content| {
3819 settings_content
3820 .status_bar
3821 .as_ref()?
3822 .active_language_button
3823 .as_ref()
3824 },
3825 write: |settings_content, value, _| {
3826 settings_content
3827 .status_bar
3828 .get_or_insert_default()
3829 .active_language_button = value;
3830 },
3831 }),
3832 metadata: None,
3833 files: USER,
3834 }),
3835 SettingsPageItem::SettingItem(SettingItem {
3836 title: "Active Encoding Button",
3837 description: "Control when to show the active encoding in the status bar.",
3838 field: Box::new(SettingField {
3839 organization_override: None,
3840 json_path: Some("status_bar.active_encoding_button"),
3841 pick: |settings_content| {
3842 settings_content
3843 .status_bar
3844 .as_ref()?
3845 .active_encoding_button
3846 .as_ref()
3847 },
3848 write: |settings_content, value, _| {
3849 settings_content
3850 .status_bar
3851 .get_or_insert_default()
3852 .active_encoding_button = value;
3853 },
3854 }),
3855 metadata: None,
3856 files: USER,
3857 }),
3858 SettingsPageItem::SettingItem(SettingItem {
3859 title: "Cursor Position Button",
3860 description: "Show the cursor position button in the status bar.",
3861 field: Box::new(SettingField {
3862 organization_override: None,
3863 json_path: Some("status_bar.cursor_position_button"),
3864 pick: |settings_content| {
3865 settings_content
3866 .status_bar
3867 .as_ref()?
3868 .cursor_position_button
3869 .as_ref()
3870 },
3871 write: |settings_content, value, _| {
3872 settings_content
3873 .status_bar
3874 .get_or_insert_default()
3875 .cursor_position_button = value;
3876 },
3877 }),
3878 metadata: None,
3879 files: USER,
3880 }),
3881 SettingsPageItem::SettingItem(SettingItem {
3882 title: "Line Endings Button",
3883 description: "Show the active line endings button in the status bar.",
3884 field: Box::new(SettingField {
3885 organization_override: None,
3886 json_path: Some("status_bar.line_endings_button"),
3887 pick: |settings_content| {
3888 settings_content
3889 .status_bar
3890 .as_ref()?
3891 .line_endings_button
3892 .as_ref()
3893 },
3894 write: |settings_content, value, _| {
3895 settings_content
3896 .status_bar
3897 .get_or_insert_default()
3898 .line_endings_button = value;
3899 },
3900 }),
3901 metadata: None,
3902 files: USER,
3903 }),
3904 SettingsPageItem::SettingItem(SettingItem {
3905 title: "Terminal Button",
3906 description: "Show the terminal button in the status bar.",
3907 field: Box::new(SettingField {
3908 organization_override: None,
3909 json_path: Some("terminal.button"),
3910 pick: |settings_content| settings_content.terminal.as_ref()?.button.as_ref(),
3911 write: |settings_content, value, _| {
3912 settings_content.terminal.get_or_insert_default().button = value;
3913 },
3914 }),
3915 metadata: None,
3916 files: USER,
3917 }),
3918 SettingsPageItem::SettingItem(SettingItem {
3919 title: "Diagnostics Button",
3920 description: "Show the project diagnostics button in the status bar.",
3921 field: Box::new(SettingField {
3922 organization_override: None,
3923 json_path: Some("diagnostics.button"),
3924 pick: |settings_content| settings_content.diagnostics.as_ref()?.button.as_ref(),
3925 write: |settings_content, value, _| {
3926 settings_content.diagnostics.get_or_insert_default().button = value;
3927 },
3928 }),
3929 metadata: None,
3930 files: USER,
3931 }),
3932 SettingsPageItem::SettingItem(SettingItem {
3933 title: "Project Search Button",
3934 description: "Show the project search button in the status bar.",
3935 field: Box::new(SettingField {
3936 organization_override: None,
3937 json_path: Some("search.button"),
3938 pick: |settings_content| {
3939 settings_content.editor.search.as_ref()?.button.as_ref()
3940 },
3941 write: |settings_content, value, _| {
3942 settings_content
3943 .editor
3944 .search
3945 .get_or_insert_default()
3946 .button = value;
3947 },
3948 }),
3949 metadata: None,
3950 files: USER,
3951 }),
3952 SettingsPageItem::SettingItem(SettingItem {
3953 title: "Debugger Button",
3954 description: "Show the debugger button in the status bar.",
3955 field: Box::new(SettingField {
3956 organization_override: None,
3957 json_path: Some("debugger.button"),
3958 pick: |settings_content| settings_content.debugger.as_ref()?.button.as_ref(),
3959 write: |settings_content, value, _| {
3960 settings_content.debugger.get_or_insert_default().button = value;
3961 },
3962 }),
3963 metadata: None,
3964 files: USER,
3965 }),
3966 SettingsPageItem::SettingItem(SettingItem {
3967 title: "Active File Name",
3968 description: "Show the name of the active file in the status bar.",
3969 field: Box::new(SettingField {
3970 organization_override: None,
3971 json_path: Some("status_bar.show_active_file"),
3972 pick: |settings_content| {
3973 settings_content
3974 .status_bar
3975 .as_ref()?
3976 .show_active_file
3977 .as_ref()
3978 },
3979 write: |settings_content, value, _| {
3980 settings_content
3981 .status_bar
3982 .get_or_insert_default()
3983 .show_active_file = value;
3984 },
3985 }),
3986 metadata: None,
3987 files: USER,
3988 }),
3989 ]
3990 }
3991
3992 fn title_bar_section() -> [SettingsPageItem; 11] {
3993 [
3994 SettingsPageItem::SectionHeader("Title Bar"),
3995 SettingsPageItem::SettingItem(SettingItem {
3996 title: "Show Branch Status Icon",
3997 description: "Show git status indicators on the branch icon in the titlebar.",
3998 field: Box::new(SettingField {
3999 organization_override: None,
4000 json_path: Some("title_bar.show_branch_status_icon"),
4001 pick: |settings_content| {
4002 settings_content
4003 .title_bar
4004 .as_ref()?
4005 .show_branch_status_icon
4006 .as_ref()
4007 },
4008 write: |settings_content, value, _| {
4009 settings_content
4010 .title_bar
4011 .get_or_insert_default()
4012 .show_branch_status_icon = value;
4013 },
4014 }),
4015 metadata: None,
4016 files: USER,
4017 }),
4018 SettingsPageItem::SettingItem(SettingItem {
4019 title: "Show Branch Name",
4020 description: "Show the branch name button in the titlebar.",
4021 field: Box::new(SettingField {
4022 organization_override: None,
4023 json_path: Some("title_bar.show_branch_name"),
4024 pick: |settings_content| {
4025 settings_content
4026 .title_bar
4027 .as_ref()?
4028 .show_branch_name
4029 .as_ref()
4030 },
4031 write: |settings_content, value, _| {
4032 settings_content
4033 .title_bar
4034 .get_or_insert_default()
4035 .show_branch_name = value;
4036 },
4037 }),
4038 metadata: None,
4039 files: USER,
4040 }),
4041 SettingsPageItem::SettingItem(SettingItem {
4042 title: "Show Worktree Name",
4043 description: "Show the worktree name button in the titlebar.",
4044 field: Box::new(SettingField {
4045 organization_override: None,
4046 json_path: Some("title_bar.show_worktree_name"),
4047 pick: |settings_content| {
4048 settings_content
4049 .title_bar
4050 .as_ref()?
4051 .show_worktree_name
4052 .as_ref()
4053 },
4054 write: |settings_content, value, _| {
4055 settings_content
4056 .title_bar
4057 .get_or_insert_default()
4058 .show_worktree_name = value;
4059 },
4060 }),
4061 metadata: None,
4062 files: USER,
4063 }),
4064 SettingsPageItem::SettingItem(SettingItem {
4065 title: "Show Project Items",
4066 description: "Show the project host and name in the titlebar.",
4067 field: Box::new(SettingField {
4068 organization_override: None,
4069 json_path: Some("title_bar.show_project_items"),
4070 pick: |settings_content| {
4071 settings_content
4072 .title_bar
4073 .as_ref()?
4074 .show_project_items
4075 .as_ref()
4076 },
4077 write: |settings_content, value, _| {
4078 settings_content
4079 .title_bar
4080 .get_or_insert_default()
4081 .show_project_items = value;
4082 },
4083 }),
4084 metadata: None,
4085 files: USER,
4086 }),
4087 SettingsPageItem::SettingItem(SettingItem {
4088 title: "Show Onboarding Banner",
4089 description: "Show banners announcing new features in the titlebar.",
4090 field: Box::new(SettingField {
4091 organization_override: None,
4092 json_path: Some("title_bar.show_onboarding_banner"),
4093 pick: |settings_content| {
4094 settings_content
4095 .title_bar
4096 .as_ref()?
4097 .show_onboarding_banner
4098 .as_ref()
4099 },
4100 write: |settings_content, value, _| {
4101 settings_content
4102 .title_bar
4103 .get_or_insert_default()
4104 .show_onboarding_banner = value;
4105 },
4106 }),
4107 metadata: None,
4108 files: USER,
4109 }),
4110 SettingsPageItem::SettingItem(SettingItem {
4111 title: "Show Sign In",
4112 description: "Show the sign in button in the titlebar.",
4113 field: Box::new(SettingField {
4114 organization_override: None,
4115 json_path: Some("title_bar.show_sign_in"),
4116 pick: |settings_content| {
4117 settings_content.title_bar.as_ref()?.show_sign_in.as_ref()
4118 },
4119 write: |settings_content, value, _| {
4120 settings_content
4121 .title_bar
4122 .get_or_insert_default()
4123 .show_sign_in = value;
4124 },
4125 }),
4126 metadata: None,
4127 files: USER,
4128 }),
4129 SettingsPageItem::SettingItem(SettingItem {
4130 title: "Show User Menu",
4131 description: "Show the user menu button in the titlebar.",
4132 field: Box::new(SettingField {
4133 organization_override: None,
4134 json_path: Some("title_bar.show_user_menu"),
4135 pick: |settings_content| {
4136 settings_content.title_bar.as_ref()?.show_user_menu.as_ref()
4137 },
4138 write: |settings_content, value, _| {
4139 settings_content
4140 .title_bar
4141 .get_or_insert_default()
4142 .show_user_menu = value;
4143 },
4144 }),
4145 metadata: None,
4146 files: USER,
4147 }),
4148 SettingsPageItem::SettingItem(SettingItem {
4149 title: "Show User Picture",
4150 description: "Show user picture in the titlebar.",
4151 field: Box::new(SettingField {
4152 organization_override: None,
4153 json_path: Some("title_bar.show_user_picture"),
4154 pick: |settings_content| {
4155 settings_content
4156 .title_bar
4157 .as_ref()?
4158 .show_user_picture
4159 .as_ref()
4160 },
4161 write: |settings_content, value, _| {
4162 settings_content
4163 .title_bar
4164 .get_or_insert_default()
4165 .show_user_picture = value;
4166 },
4167 }),
4168 metadata: None,
4169 files: USER,
4170 }),
4171 SettingsPageItem::SettingItem(SettingItem {
4172 title: "Show Menus",
4173 description: "Show the menus in the titlebar.",
4174 field: Box::new(SettingField {
4175 organization_override: None,
4176 json_path: Some("title_bar.show_menus"),
4177 pick: |settings_content| {
4178 settings_content.title_bar.as_ref()?.show_menus.as_ref()
4179 },
4180 write: |settings_content, value, _| {
4181 settings_content
4182 .title_bar
4183 .get_or_insert_default()
4184 .show_menus = value;
4185 },
4186 }),
4187 metadata: None,
4188 files: USER,
4189 }),
4190 SettingsPageItem::DynamicItem(DynamicItem {
4191 discriminant: SettingItem {
4192 files: USER,
4193 title: "Button Layout",
4194 description:
4195 "(Linux only) choose how window control buttons are laid out in the titlebar.",
4196 field: Box::new(SettingField {
4197 organization_override: None,
4198 json_path: Some("title_bar.button_layout$"),
4199 pick: |settings_content| {
4200 Some(
4201 &dynamic_variants::<settings::WindowButtonLayoutContent>()[settings_content
4202 .title_bar
4203 .as_ref()?
4204 .button_layout
4205 .as_ref()?
4206 .discriminant()
4207 as usize],
4208 )
4209 },
4210 write: |settings_content, value, _| {
4211 let Some(value) = value else {
4212 settings_content
4213 .title_bar
4214 .get_or_insert_default()
4215 .button_layout = None;
4216 return;
4217 };
4218
4219 let current_custom_layout = settings_content
4220 .title_bar
4221 .as_ref()
4222 .and_then(|title_bar| title_bar.button_layout.as_ref())
4223 .and_then(|button_layout| match button_layout {
4224 settings::WindowButtonLayoutContent::Custom(layout) => {
4225 Some(layout.clone())
4226 }
4227 _ => None,
4228 });
4229
4230 let button_layout = match value {
4231 settings::WindowButtonLayoutContentDiscriminants::PlatformDefault => {
4232 settings::WindowButtonLayoutContent::PlatformDefault
4233 }
4234 settings::WindowButtonLayoutContentDiscriminants::Standard => {
4235 settings::WindowButtonLayoutContent::Standard
4236 }
4237 settings::WindowButtonLayoutContentDiscriminants::Custom => {
4238 settings::WindowButtonLayoutContent::Custom(
4239 current_custom_layout.unwrap_or_else(|| {
4240 "close:minimize,maximize".to_string()
4241 }),
4242 )
4243 }
4244 };
4245
4246 settings_content
4247 .title_bar
4248 .get_or_insert_default()
4249 .button_layout = Some(button_layout);
4250 },
4251 }),
4252 metadata: None,
4253 },
4254 pick_discriminant: |settings_content| {
4255 Some(
4256 settings_content
4257 .title_bar
4258 .as_ref()?
4259 .button_layout
4260 .as_ref()?
4261 .discriminant() as usize,
4262 )
4263 },
4264 fields: dynamic_variants::<settings::WindowButtonLayoutContent>()
4265 .into_iter()
4266 .map(|variant| match variant {
4267 settings::WindowButtonLayoutContentDiscriminants::PlatformDefault => {
4268 vec![]
4269 }
4270 settings::WindowButtonLayoutContentDiscriminants::Standard => vec![],
4271 settings::WindowButtonLayoutContentDiscriminants::Custom => vec![
4272 SettingItem {
4273 files: USER,
4274 title: "Custom Button Layout",
4275 description:
4276 "GNOME-style layout string such as \"close:minimize,maximize\".",
4277 field: Box::new(SettingField {
4278 organization_override: None,
4279 json_path: Some("title_bar.button_layout"),
4280 pick: |settings_content| match settings_content
4281 .title_bar
4282 .as_ref()?
4283 .button_layout
4284 .as_ref()?
4285 {
4286 settings::WindowButtonLayoutContent::Custom(layout) => {
4287 Some(layout)
4288 }
4289 _ => DEFAULT_EMPTY_STRING,
4290 },
4291 write: |settings_content, value, _| {
4292 settings_content
4293 .title_bar
4294 .get_or_insert_default()
4295 .button_layout = value
4296 .map(settings::WindowButtonLayoutContent::Custom);
4297 },
4298 }),
4299 metadata: Some(Box::new(SettingsFieldMetadata {
4300 placeholder: Some("close:minimize,maximize"),
4301 ..Default::default()
4302 })),
4303 },
4304 ],
4305 })
4306 .collect(),
4307 }),
4308 ]
4309 }
4310
4311 fn tab_bar_section() -> [SettingsPageItem; 9] {
4312 [
4313 SettingsPageItem::SectionHeader("Tab Bar"),
4314 SettingsPageItem::SettingItem(SettingItem {
4315 title: "Show Tab Bar",
4316 description: "Show the tab bar in the editor.",
4317 field: Box::new(SettingField {
4318 organization_override: None,
4319 json_path: Some("tab_bar.show"),
4320 pick: |settings_content| settings_content.tab_bar.as_ref()?.show.as_ref(),
4321 write: |settings_content, value, _| {
4322 settings_content.tab_bar.get_or_insert_default().show = value;
4323 },
4324 }),
4325 metadata: None,
4326 files: USER,
4327 }),
4328 SettingsPageItem::SettingItem(SettingItem {
4329 title: "Show Git Status In Tabs",
4330 description: "Show the Git file status on a tab item.",
4331 field: Box::new(SettingField {
4332 organization_override: None,
4333 json_path: Some("tabs.git_status"),
4334 pick: |settings_content| settings_content.tabs.as_ref()?.git_status.as_ref(),
4335 write: |settings_content, value, _| {
4336 settings_content.tabs.get_or_insert_default().git_status = value;
4337 },
4338 }),
4339 metadata: None,
4340 files: USER,
4341 }),
4342 SettingsPageItem::SettingItem(SettingItem {
4343 title: "Show File Icons In Tabs",
4344 description: "Show the file icon for a tab.",
4345 field: Box::new(SettingField {
4346 organization_override: None,
4347 json_path: Some("tabs.file_icons"),
4348 pick: |settings_content| settings_content.tabs.as_ref()?.file_icons.as_ref(),
4349 write: |settings_content, value, _| {
4350 settings_content.tabs.get_or_insert_default().file_icons = value;
4351 },
4352 }),
4353 metadata: None,
4354 files: USER,
4355 }),
4356 SettingsPageItem::SettingItem(SettingItem {
4357 title: "Tab Close Position",
4358 description: "Position of the close button in a tab.",
4359 field: Box::new(SettingField {
4360 organization_override: None,
4361 json_path: Some("tabs.close_position"),
4362 pick: |settings_content| {
4363 settings_content.tabs.as_ref()?.close_position.as_ref()
4364 },
4365 write: |settings_content, value, _| {
4366 settings_content.tabs.get_or_insert_default().close_position = value;
4367 },
4368 }),
4369 metadata: None,
4370 files: USER,
4371 }),
4372 SettingsPageItem::SettingItem(SettingItem {
4373 files: USER,
4374 title: "Maximum Tabs",
4375 description: "Maximum open tabs in a pane. Will not close an unsaved tab.",
4376 // todo(settings_ui): The default for this value is null and it's use in code
4377 // is complex, so I'm going to come back to this later
4378 field: Box::new(
4379 SettingField {
4380 organization_override: None,
4381 json_path: Some("max_tabs"),
4382 pick: |settings_content| settings_content.workspace.max_tabs.as_ref(),
4383 write: |settings_content, value, _| {
4384 settings_content.workspace.max_tabs = value;
4385 },
4386 }
4387 .unimplemented(),
4388 ),
4389 metadata: None,
4390 }),
4391 SettingsPageItem::SettingItem(SettingItem {
4392 title: "Show Navigation History Buttons",
4393 description: "Show the navigation history buttons in the tab bar.",
4394 field: Box::new(SettingField {
4395 organization_override: None,
4396 json_path: Some("tab_bar.show_nav_history_buttons"),
4397 pick: |settings_content| {
4398 settings_content
4399 .tab_bar
4400 .as_ref()?
4401 .show_nav_history_buttons
4402 .as_ref()
4403 },
4404 write: |settings_content, value, _| {
4405 settings_content
4406 .tab_bar
4407 .get_or_insert_default()
4408 .show_nav_history_buttons = value;
4409 },
4410 }),
4411 metadata: None,
4412 files: USER,
4413 }),
4414 SettingsPageItem::SettingItem(SettingItem {
4415 title: "Show Tab Bar Buttons",
4416 description: "Show the tab bar buttons (New, Split Pane, Zoom).",
4417 field: Box::new(SettingField {
4418 organization_override: None,
4419 json_path: Some("tab_bar.show_tab_bar_buttons"),
4420 pick: |settings_content| {
4421 settings_content
4422 .tab_bar
4423 .as_ref()?
4424 .show_tab_bar_buttons
4425 .as_ref()
4426 },
4427 write: |settings_content, value, _| {
4428 settings_content
4429 .tab_bar
4430 .get_or_insert_default()
4431 .show_tab_bar_buttons = value;
4432 },
4433 }),
4434 metadata: None,
4435 files: USER,
4436 }),
4437 SettingsPageItem::SettingItem(SettingItem {
4438 title: "Pinned Tabs Layout",
4439 description: "Show pinned tabs in a separate row above unpinned tabs.",
4440 field: Box::new(SettingField {
4441 organization_override: None,
4442 json_path: Some("tab_bar.show_pinned_tabs_in_separate_row"),
4443 pick: |settings_content| {
4444 settings_content
4445 .tab_bar
4446 .as_ref()?
4447 .show_pinned_tabs_in_separate_row
4448 .as_ref()
4449 },
4450 write: |settings_content, value, _| {
4451 settings_content
4452 .tab_bar
4453 .get_or_insert_default()
4454 .show_pinned_tabs_in_separate_row = value;
4455 },
4456 }),
4457 metadata: None,
4458 files: USER,
4459 }),
4460 ]
4461 }
4462
4463 fn tab_settings_section() -> [SettingsPageItem; 4] {
4464 [
4465 SettingsPageItem::SectionHeader("Tab Settings"),
4466 SettingsPageItem::SettingItem(SettingItem {
4467 title: "Activate On Close",
4468 description: "What to do after closing the current tab.",
4469 field: Box::new(SettingField {
4470 organization_override: None,
4471 json_path: Some("tabs.activate_on_close"),
4472 pick: |settings_content| {
4473 settings_content.tabs.as_ref()?.activate_on_close.as_ref()
4474 },
4475 write: |settings_content, value, _| {
4476 settings_content
4477 .tabs
4478 .get_or_insert_default()
4479 .activate_on_close = value;
4480 },
4481 }),
4482 metadata: None,
4483 files: USER,
4484 }),
4485 SettingsPageItem::SettingItem(SettingItem {
4486 title: "Tab Show Diagnostics",
4487 description: "Which files containing diagnostic errors/warnings to mark in the tabs.",
4488 field: Box::new(SettingField {
4489 organization_override: None,
4490 json_path: Some("tabs.show_diagnostics"),
4491 pick: |settings_content| {
4492 settings_content.tabs.as_ref()?.show_diagnostics.as_ref()
4493 },
4494 write: |settings_content, value, _| {
4495 settings_content
4496 .tabs
4497 .get_or_insert_default()
4498 .show_diagnostics = value;
4499 },
4500 }),
4501 metadata: None,
4502 files: USER,
4503 }),
4504 SettingsPageItem::SettingItem(SettingItem {
4505 title: "Show Close Button",
4506 description: "Controls the appearance behavior of the tab's close button.",
4507 field: Box::new(SettingField {
4508 organization_override: None,
4509 json_path: Some("tabs.show_close_button"),
4510 pick: |settings_content| {
4511 settings_content.tabs.as_ref()?.show_close_button.as_ref()
4512 },
4513 write: |settings_content, value, _| {
4514 settings_content
4515 .tabs
4516 .get_or_insert_default()
4517 .show_close_button = value;
4518 },
4519 }),
4520 metadata: None,
4521 files: USER,
4522 }),
4523 ]
4524 }
4525
4526 fn preview_tabs_section() -> [SettingsPageItem; 8] {
4527 [
4528 SettingsPageItem::SectionHeader("Preview Tabs"),
4529 SettingsPageItem::SettingItem(SettingItem {
4530 title: "Preview Tabs Enabled",
4531 description: "Show opened editors as preview tabs.",
4532 field: Box::new(SettingField {
4533 organization_override: None,
4534 json_path: Some("preview_tabs.enabled"),
4535 pick: |settings_content| {
4536 settings_content.preview_tabs.as_ref()?.enabled.as_ref()
4537 },
4538 write: |settings_content, value, _| {
4539 settings_content
4540 .preview_tabs
4541 .get_or_insert_default()
4542 .enabled = value;
4543 },
4544 }),
4545 metadata: None,
4546 files: USER,
4547 }),
4548 SettingsPageItem::SettingItem(SettingItem {
4549 title: "Enable Preview From Project Panel",
4550 description: "Whether to open tabs in preview mode when opened from the project panel with a single click.",
4551 field: Box::new(SettingField {
4552 organization_override: None,
4553 json_path: Some("preview_tabs.enable_preview_from_project_panel"),
4554 pick: |settings_content| {
4555 settings_content
4556 .preview_tabs
4557 .as_ref()?
4558 .enable_preview_from_project_panel
4559 .as_ref()
4560 },
4561 write: |settings_content, value, _| {
4562 settings_content
4563 .preview_tabs
4564 .get_or_insert_default()
4565 .enable_preview_from_project_panel = value;
4566 },
4567 }),
4568 metadata: None,
4569 files: USER,
4570 }),
4571 SettingsPageItem::SettingItem(SettingItem {
4572 title: "Enable Preview From File Finder",
4573 description: "Whether to open tabs in preview mode when selected from the file finder.",
4574 field: Box::new(SettingField {
4575 organization_override: None,
4576 json_path: Some("preview_tabs.enable_preview_from_file_finder"),
4577 pick: |settings_content| {
4578 settings_content
4579 .preview_tabs
4580 .as_ref()?
4581 .enable_preview_from_file_finder
4582 .as_ref()
4583 },
4584 write: |settings_content, value, _| {
4585 settings_content
4586 .preview_tabs
4587 .get_or_insert_default()
4588 .enable_preview_from_file_finder = value;
4589 },
4590 }),
4591 metadata: None,
4592 files: USER,
4593 }),
4594 SettingsPageItem::SettingItem(SettingItem {
4595 title: "Enable Preview From Multibuffer",
4596 description: "Whether to open tabs in preview mode when opened from a multibuffer.",
4597 field: Box::new(SettingField {
4598 organization_override: None,
4599 json_path: Some("preview_tabs.enable_preview_from_multibuffer"),
4600 pick: |settings_content| {
4601 settings_content
4602 .preview_tabs
4603 .as_ref()?
4604 .enable_preview_from_multibuffer
4605 .as_ref()
4606 },
4607 write: |settings_content, value, _| {
4608 settings_content
4609 .preview_tabs
4610 .get_or_insert_default()
4611 .enable_preview_from_multibuffer = value;
4612 },
4613 }),
4614 metadata: None,
4615 files: USER,
4616 }),
4617 SettingsPageItem::SettingItem(SettingItem {
4618 title: "Enable Preview Multibuffer From Code Navigation",
4619 description: "Whether to open tabs in preview mode when code navigation is used to open a multibuffer.",
4620 field: Box::new(SettingField {
4621 organization_override: None,
4622 json_path: Some("preview_tabs.enable_preview_multibuffer_from_code_navigation"),
4623 pick: |settings_content| {
4624 settings_content
4625 .preview_tabs
4626 .as_ref()?
4627 .enable_preview_multibuffer_from_code_navigation
4628 .as_ref()
4629 },
4630 write: |settings_content, value, _| {
4631 settings_content
4632 .preview_tabs
4633 .get_or_insert_default()
4634 .enable_preview_multibuffer_from_code_navigation = value;
4635 },
4636 }),
4637 metadata: None,
4638 files: USER,
4639 }),
4640 SettingsPageItem::SettingItem(SettingItem {
4641 title: "Enable Preview File From Code Navigation",
4642 description: "Whether to open tabs in preview mode when code navigation is used to open a single file.",
4643 field: Box::new(SettingField {
4644 organization_override: None,
4645 json_path: Some("preview_tabs.enable_preview_file_from_code_navigation"),
4646 pick: |settings_content| {
4647 settings_content
4648 .preview_tabs
4649 .as_ref()?
4650 .enable_preview_file_from_code_navigation
4651 .as_ref()
4652 },
4653 write: |settings_content, value, _| {
4654 settings_content
4655 .preview_tabs
4656 .get_or_insert_default()
4657 .enable_preview_file_from_code_navigation = value;
4658 },
4659 }),
4660 metadata: None,
4661 files: USER,
4662 }),
4663 SettingsPageItem::SettingItem(SettingItem {
4664 title: "Enable Keep Preview On Code Navigation",
4665 description: "Whether to keep tabs in preview mode when code navigation is used to navigate away from them. If `enable_preview_file_from_code_navigation` or `enable_preview_multibuffer_from_code_navigation` is also true, the new tab may replace the existing one.",
4666 field: Box::new(SettingField {
4667 organization_override: None,
4668 json_path: Some("preview_tabs.enable_keep_preview_on_code_navigation"),
4669 pick: |settings_content| {
4670 settings_content
4671 .preview_tabs
4672 .as_ref()?
4673 .enable_keep_preview_on_code_navigation
4674 .as_ref()
4675 },
4676 write: |settings_content, value, _| {
4677 settings_content
4678 .preview_tabs
4679 .get_or_insert_default()
4680 .enable_keep_preview_on_code_navigation = value;
4681 },
4682 }),
4683 metadata: None,
4684 files: USER,
4685 }),
4686 ]
4687 }
4688
4689 fn layout_section() -> [SettingsPageItem; 6] {
4690 [
4691 SettingsPageItem::SectionHeader("Layout"),
4692 SettingsPageItem::SettingItem(SettingItem {
4693 title: "Bottom Dock Layout",
4694 description: "Layout mode for the bottom dock.",
4695 field: Box::new(SettingField {
4696 organization_override: None,
4697 json_path: Some("bottom_dock_layout"),
4698 pick: |settings_content| settings_content.workspace.bottom_dock_layout.as_ref(),
4699 write: |settings_content, value, _| {
4700 settings_content.workspace.bottom_dock_layout = value;
4701 },
4702 }),
4703 metadata: None,
4704 files: USER,
4705 }),
4706 SettingsPageItem::SettingItem(SettingItem {
4707 files: USER,
4708 title: "Centered Layout Left Padding",
4709 description: "Left padding for centered layout.",
4710 field: Box::new(SettingField {
4711 organization_override: None,
4712 json_path: Some("centered_layout.left_padding"),
4713 pick: |settings_content| {
4714 settings_content
4715 .workspace
4716 .centered_layout
4717 .as_ref()?
4718 .left_padding
4719 .as_ref()
4720 },
4721 write: |settings_content, value, _| {
4722 settings_content
4723 .workspace
4724 .centered_layout
4725 .get_or_insert_default()
4726 .left_padding = value;
4727 },
4728 }),
4729 metadata: None,
4730 }),
4731 SettingsPageItem::SettingItem(SettingItem {
4732 files: USER,
4733 title: "Centered Layout Right Padding",
4734 description: "Right padding for centered layout.",
4735 field: Box::new(SettingField {
4736 organization_override: None,
4737 json_path: Some("centered_layout.right_padding"),
4738 pick: |settings_content| {
4739 settings_content
4740 .workspace
4741 .centered_layout
4742 .as_ref()?
4743 .right_padding
4744 .as_ref()
4745 },
4746 write: |settings_content, value, _| {
4747 settings_content
4748 .workspace
4749 .centered_layout
4750 .get_or_insert_default()
4751 .right_padding = value;
4752 },
4753 }),
4754 metadata: None,
4755 }),
4756 SettingsPageItem::SettingItem(SettingItem {
4757 title: "Focus Follows Mouse",
4758 description: "Whether to change focus to a pane when the mouse hovers over it.",
4759 field: Box::new(SettingField {
4760 organization_override: None,
4761 json_path: Some("focus_follows_mouse.enabled"),
4762 pick: |settings_content| {
4763 settings_content
4764 .workspace
4765 .focus_follows_mouse
4766 .as_ref()
4767 .and_then(|s| s.enabled.as_ref())
4768 },
4769 write: |settings_content, value, _| {
4770 settings_content
4771 .workspace
4772 .focus_follows_mouse
4773 .get_or_insert_default()
4774 .enabled = value;
4775 },
4776 }),
4777 metadata: None,
4778 files: USER,
4779 }),
4780 SettingsPageItem::SettingItem(SettingItem {
4781 title: "Focus Follows Mouse Debounce ms",
4782 description: "Amount of time to wait before changing focus.",
4783 field: Box::new(SettingField {
4784 organization_override: None,
4785 json_path: Some("focus_follows_mouse.debounce_ms"),
4786 pick: |settings_content| {
4787 settings_content
4788 .workspace
4789 .focus_follows_mouse
4790 .as_ref()
4791 .and_then(|s| s.debounce_ms.as_ref())
4792 },
4793 write: |settings_content, value, _| {
4794 settings_content
4795 .workspace
4796 .focus_follows_mouse
4797 .get_or_insert_default()
4798 .debounce_ms = value;
4799 },
4800 }),
4801 metadata: None,
4802 files: USER,
4803 }),
4804 ]
4805 }
4806
4807 fn window_section() -> [SettingsPageItem; 3] {
4808 [
4809 SettingsPageItem::SectionHeader("Window"),
4810 // todo(settings_ui): Should we filter by platform.as_ref()?
4811 SettingsPageItem::SettingItem(SettingItem {
4812 title: "Use System Window Tabs",
4813 description: "(macOS only) whether to allow Windows to tab together.",
4814 field: Box::new(SettingField {
4815 organization_override: None,
4816 json_path: Some("use_system_window_tabs"),
4817 pick: |settings_content| {
4818 settings_content.workspace.use_system_window_tabs.as_ref()
4819 },
4820 write: |settings_content, value, _| {
4821 settings_content.workspace.use_system_window_tabs = value;
4822 },
4823 }),
4824 metadata: None,
4825 files: USER,
4826 }),
4827 SettingsPageItem::SettingItem(SettingItem {
4828 title: "Window Decorations",
4829 description: "(Linux only) whether Omega or your compositor should draw window decorations.",
4830 field: Box::new(SettingField {
4831 organization_override: None,
4832 json_path: Some("window_decorations"),
4833 pick: |settings_content| settings_content.workspace.window_decorations.as_ref(),
4834 write: |settings_content, value, _| {
4835 settings_content.workspace.window_decorations = value;
4836 },
4837 }),
4838 metadata: None,
4839 files: USER,
4840 }),
4841 ]
4842 }
4843
4844 fn pane_modifiers_section() -> [SettingsPageItem; 4] {
4845 [
4846 SettingsPageItem::SectionHeader("Pane Modifiers"),
4847 SettingsPageItem::SettingItem(SettingItem {
4848 title: "Inactive Opacity",
4849 description: "Opacity of inactive panels (0.0 - 1.0).",
4850 field: Box::new(SettingField {
4851 organization_override: None,
4852 json_path: Some("active_pane_modifiers.inactive_opacity"),
4853 pick: |settings_content| {
4854 settings_content
4855 .workspace
4856 .active_pane_modifiers
4857 .as_ref()?
4858 .inactive_opacity
4859 .as_ref()
4860 },
4861 write: |settings_content, value, _| {
4862 settings_content
4863 .workspace
4864 .active_pane_modifiers
4865 .get_or_insert_default()
4866 .inactive_opacity = value;
4867 },
4868 }),
4869 metadata: None,
4870 files: USER,
4871 }),
4872 SettingsPageItem::SettingItem(SettingItem {
4873 title: "Border Size",
4874 description: "Size of the border surrounding the active pane.",
4875 field: Box::new(SettingField {
4876 organization_override: None,
4877 json_path: Some("active_pane_modifiers.border_size"),
4878 pick: |settings_content| {
4879 settings_content
4880 .workspace
4881 .active_pane_modifiers
4882 .as_ref()?
4883 .border_size
4884 .as_ref()
4885 },
4886 write: |settings_content, value, _| {
4887 settings_content
4888 .workspace
4889 .active_pane_modifiers
4890 .get_or_insert_default()
4891 .border_size = value;
4892 },
4893 }),
4894 metadata: None,
4895 files: USER,
4896 }),
4897 SettingsPageItem::SettingItem(SettingItem {
4898 title: "Zoomed Padding",
4899 description: "Show padding for zoomed panes.",
4900 field: Box::new(SettingField {
4901 organization_override: None,
4902 json_path: Some("zoomed_padding"),
4903 pick: |settings_content| settings_content.workspace.zoomed_padding.as_ref(),
4904 write: |settings_content, value, _| {
4905 settings_content.workspace.zoomed_padding = value;
4906 },
4907 }),
4908 metadata: None,
4909 files: USER,
4910 }),
4911 ]
4912 }
4913
4914 fn pane_split_direction_section() -> [SettingsPageItem; 3] {
4915 [
4916 SettingsPageItem::SectionHeader("Pane Split Direction"),
4917 SettingsPageItem::SettingItem(SettingItem {
4918 title: "Vertical Split Direction",
4919 description: "Direction to split vertically.",
4920 field: Box::new(SettingField {
4921 organization_override: None,
4922 json_path: Some("pane_split_direction_vertical"),
4923 pick: |settings_content| {
4924 settings_content
4925 .workspace
4926 .pane_split_direction_vertical
4927 .as_ref()
4928 },
4929 write: |settings_content, value, _| {
4930 settings_content.workspace.pane_split_direction_vertical = value;
4931 },
4932 }),
4933 metadata: None,
4934 files: USER,
4935 }),
4936 SettingsPageItem::SettingItem(SettingItem {
4937 title: "Horizontal Split Direction",
4938 description: "Direction to split horizontally.",
4939 field: Box::new(SettingField {
4940 organization_override: None,
4941 json_path: Some("pane_split_direction_horizontal"),
4942 pick: |settings_content| {
4943 settings_content
4944 .workspace
4945 .pane_split_direction_horizontal
4946 .as_ref()
4947 },
4948 write: |settings_content, value, _| {
4949 settings_content.workspace.pane_split_direction_horizontal = value;
4950 },
4951 }),
4952 metadata: None,
4953 files: USER,
4954 }),
4955 ]
4956 }
4957
4958 SettingsPage {
4959 title: "Window & Layout",
4960 items: concat_sections![
4961 status_bar_section(),
4962 title_bar_section(),
4963 tab_bar_section(),
4964 tab_settings_section(),
4965 preview_tabs_section(),
4966 layout_section(),
4967 window_section(),
4968 pane_modifiers_section(),
4969 pane_split_direction_section(),
4970 ],
4971 }
4972}
4973
4974fn panels_page() -> SettingsPage {
4975 fn project_panel_section() -> [SettingsPageItem; 29] {
4976 [
4977 SettingsPageItem::SectionHeader("Project Panel"),
4978 SettingsPageItem::SettingItem(SettingItem {
4979 title: "Project Panel Dock",
4980 description: "Where to dock the project panel.",
4981 field: Box::new(SettingField {
4982 organization_override: None,
4983 json_path: Some("project_panel.dock"),
4984 pick: |settings_content| settings_content.project_panel.as_ref()?.dock.as_ref(),
4985 write: |settings_content, value, _| {
4986 settings_content.project_panel.get_or_insert_default().dock = value;
4987 },
4988 }),
4989 metadata: None,
4990 files: USER,
4991 }),
4992 SettingsPageItem::SettingItem(SettingItem {
4993 title: "Project Panel Default Width",
4994 description: "Default width of the project panel in pixels.",
4995 field: Box::new(SettingField {
4996 organization_override: None,
4997 json_path: Some("project_panel.default_width"),
4998 pick: |settings_content| {
4999 settings_content
5000 .project_panel
5001 .as_ref()?
5002 .default_width
5003 .as_ref()
5004 },
5005 write: |settings_content, value, _| {
5006 settings_content
5007 .project_panel
5008 .get_or_insert_default()
5009 .default_width = value;
5010 },
5011 }),
5012 metadata: None,
5013 files: USER,
5014 }),
5015 SettingsPageItem::SettingItem(SettingItem {
5016 title: "Hide .gitignore",
5017 description: "Whether to hide the gitignore entries in the project panel.",
5018 field: Box::new(SettingField {
5019 organization_override: None,
5020 json_path: Some("project_panel.hide_gitignore"),
5021 pick: |settings_content| {
5022 settings_content
5023 .project_panel
5024 .as_ref()?
5025 .hide_gitignore
5026 .as_ref()
5027 },
5028 write: |settings_content, value, _| {
5029 settings_content
5030 .project_panel
5031 .get_or_insert_default()
5032 .hide_gitignore = value;
5033 },
5034 }),
5035 metadata: None,
5036 files: USER,
5037 }),
5038 SettingsPageItem::SettingItem(SettingItem {
5039 title: "Entry Spacing",
5040 description: "Spacing between worktree entries in the project panel.",
5041 field: Box::new(SettingField {
5042 organization_override: None,
5043 json_path: Some("project_panel.entry_spacing"),
5044 pick: |settings_content| {
5045 settings_content
5046 .project_panel
5047 .as_ref()?
5048 .entry_spacing
5049 .as_ref()
5050 },
5051 write: |settings_content, value, _| {
5052 settings_content
5053 .project_panel
5054 .get_or_insert_default()
5055 .entry_spacing = value;
5056 },
5057 }),
5058 metadata: None,
5059 files: USER,
5060 }),
5061 SettingsPageItem::SettingItem(SettingItem {
5062 title: "File Icons",
5063 description: "Show file icons in the project panel.",
5064 field: Box::new(SettingField {
5065 organization_override: None,
5066 json_path: Some("project_panel.file_icons"),
5067 pick: |settings_content| {
5068 settings_content.project_panel.as_ref()?.file_icons.as_ref()
5069 },
5070 write: |settings_content, value, _| {
5071 settings_content
5072 .project_panel
5073 .get_or_insert_default()
5074 .file_icons = value;
5075 },
5076 }),
5077 metadata: None,
5078 files: USER,
5079 }),
5080 SettingsPageItem::SettingItem(SettingItem {
5081 title: "Folder Icons",
5082 description: "Whether to show folder icons or chevrons for directories in the project panel.",
5083 field: Box::new(SettingField {
5084 organization_override: None,
5085 json_path: Some("project_panel.folder_icons"),
5086 pick: |settings_content| {
5087 settings_content
5088 .project_panel
5089 .as_ref()?
5090 .folder_icons
5091 .as_ref()
5092 },
5093 write: |settings_content, value, _| {
5094 settings_content
5095 .project_panel
5096 .get_or_insert_default()
5097 .folder_icons = value;
5098 },
5099 }),
5100 metadata: None,
5101 files: USER,
5102 }),
5103 SettingsPageItem::SettingItem(SettingItem {
5104 title: "Git Status",
5105 description: "Show the Git status in the project panel.",
5106 field: Box::new(SettingField {
5107 organization_override: None,
5108 json_path: Some("project_panel.git_status"),
5109 pick: |settings_content| {
5110 settings_content.project_panel.as_ref()?.git_status.as_ref()
5111 },
5112 write: |settings_content, value, _| {
5113 settings_content
5114 .project_panel
5115 .get_or_insert_default()
5116 .git_status = value;
5117 },
5118 }),
5119 metadata: None,
5120 files: USER,
5121 }),
5122 SettingsPageItem::SettingItem(SettingItem {
5123 title: "Indent Size",
5124 description: "Amount of indentation for nested items.",
5125 field: Box::new(SettingField {
5126 organization_override: None,
5127 json_path: Some("project_panel.indent_size"),
5128 pick: |settings_content| {
5129 settings_content
5130 .project_panel
5131 .as_ref()?
5132 .indent_size
5133 .as_ref()
5134 },
5135 write: |settings_content, value, _| {
5136 settings_content
5137 .project_panel
5138 .get_or_insert_default()
5139 .indent_size = value;
5140 },
5141 }),
5142 metadata: None,
5143 files: USER,
5144 }),
5145 SettingsPageItem::SettingItem(SettingItem {
5146 title: "Auto Reveal Entries",
5147 description: "Whether to reveal entries in the project panel automatically when a corresponding project entry becomes active.",
5148 field: Box::new(SettingField {
5149 organization_override: None,
5150 json_path: Some("project_panel.auto_reveal_entries"),
5151 pick: |settings_content| {
5152 settings_content
5153 .project_panel
5154 .as_ref()?
5155 .auto_reveal_entries
5156 .as_ref()
5157 },
5158 write: |settings_content, value, _| {
5159 settings_content
5160 .project_panel
5161 .get_or_insert_default()
5162 .auto_reveal_entries = value;
5163 },
5164 }),
5165 metadata: None,
5166 files: USER,
5167 }),
5168 SettingsPageItem::SettingItem(SettingItem {
5169 title: "Starts Open",
5170 description: "Whether the project panel should open on startup.",
5171 field: Box::new(SettingField {
5172 organization_override: None,
5173 json_path: Some("project_panel.starts_open"),
5174 pick: |settings_content| {
5175 settings_content
5176 .project_panel
5177 .as_ref()?
5178 .starts_open
5179 .as_ref()
5180 },
5181 write: |settings_content, value, _| {
5182 settings_content
5183 .project_panel
5184 .get_or_insert_default()
5185 .starts_open = value;
5186 },
5187 }),
5188 metadata: None,
5189 files: USER,
5190 }),
5191 SettingsPageItem::SettingItem(SettingItem {
5192 title: "Auto Fold Directories",
5193 description: "Whether to fold directories automatically and show compact folders when a directory has only one subdirectory inside.",
5194 field: Box::new(SettingField {
5195 organization_override: None,
5196 json_path: Some("project_panel.auto_fold_dirs"),
5197 pick: |settings_content| {
5198 settings_content
5199 .project_panel
5200 .as_ref()?
5201 .auto_fold_dirs
5202 .as_ref()
5203 },
5204 write: |settings_content, value, _| {
5205 settings_content
5206 .project_panel
5207 .get_or_insert_default()
5208 .auto_fold_dirs = value;
5209 },
5210 }),
5211 metadata: None,
5212 files: USER,
5213 }),
5214 SettingsPageItem::SettingItem(SettingItem {
5215 title: "Bold Folder Labels",
5216 description: "Whether to show folder names with bold text in the project panel.",
5217 field: Box::new(SettingField {
5218 organization_override: None,
5219 json_path: Some("project_panel.bold_folder_labels"),
5220 pick: |settings_content| {
5221 settings_content
5222 .project_panel
5223 .as_ref()?
5224 .bold_folder_labels
5225 .as_ref()
5226 },
5227 write: |settings_content, value, _| {
5228 settings_content
5229 .project_panel
5230 .get_or_insert_default()
5231 .bold_folder_labels = value;
5232 },
5233 }),
5234 metadata: None,
5235 files: USER,
5236 }),
5237 SettingsPageItem::SettingItem(SettingItem {
5238 title: "Show Scrollbar",
5239 description: "Show the scrollbar in the project panel.",
5240 field: Box::new(SettingField {
5241 organization_override: None,
5242 json_path: Some("project_panel.scrollbar.show"),
5243 pick: |settings_content| {
5244 show_scrollbar_or_editor(settings_content, |settings_content| {
5245 settings_content
5246 .project_panel
5247 .as_ref()?
5248 .scrollbar
5249 .as_ref()?
5250 .show
5251 .as_ref()
5252 })
5253 },
5254 write: |settings_content, value, _| {
5255 settings_content
5256 .project_panel
5257 .get_or_insert_default()
5258 .scrollbar
5259 .get_or_insert_default()
5260 .show = value;
5261 },
5262 }),
5263 metadata: None,
5264 files: USER,
5265 }),
5266 SettingsPageItem::SettingItem(SettingItem {
5267 title: "Horizontal Scroll",
5268 description: "Whether to allow horizontal scrolling in the project panel. When disabled, the view is always locked to the leftmost position and long file names are clipped.",
5269 field: Box::new(SettingField {
5270 organization_override: None,
5271 json_path: Some("project_panel.scrollbar.horizontal_scroll"),
5272 pick: |settings_content| {
5273 settings_content
5274 .project_panel
5275 .as_ref()?
5276 .scrollbar
5277 .as_ref()?
5278 .horizontal_scroll
5279 .as_ref()
5280 },
5281 write: |settings_content, value, _| {
5282 settings_content
5283 .project_panel
5284 .get_or_insert_default()
5285 .scrollbar
5286 .get_or_insert_default()
5287 .horizontal_scroll = value;
5288 },
5289 }),
5290 metadata: None,
5291 files: USER,
5292 }),
5293 SettingsPageItem::SettingItem(SettingItem {
5294 title: "Show Diagnostics",
5295 description: "Which files containing diagnostic errors/warnings to mark in the project panel.",
5296 field: Box::new(SettingField {
5297 organization_override: None,
5298 json_path: Some("project_panel.show_diagnostics"),
5299 pick: |settings_content| {
5300 settings_content
5301 .project_panel
5302 .as_ref()?
5303 .show_diagnostics
5304 .as_ref()
5305 },
5306 write: |settings_content, value, _| {
5307 settings_content
5308 .project_panel
5309 .get_or_insert_default()
5310 .show_diagnostics = value;
5311 },
5312 }),
5313 metadata: None,
5314 files: USER,
5315 }),
5316 SettingsPageItem::SettingItem(SettingItem {
5317 title: "Diagnostic Badges",
5318 description: "Show error and warning count badges next to file names in the project panel.",
5319 field: Box::new(SettingField {
5320 organization_override: None,
5321 json_path: Some("project_panel.diagnostic_badges"),
5322 pick: |settings_content| {
5323 settings_content
5324 .project_panel
5325 .as_ref()?
5326 .diagnostic_badges
5327 .as_ref()
5328 },
5329 write: |settings_content, value, _| {
5330 settings_content
5331 .project_panel
5332 .get_or_insert_default()
5333 .diagnostic_badges = value;
5334 },
5335 }),
5336 metadata: None,
5337 files: USER,
5338 }),
5339 SettingsPageItem::SettingItem(SettingItem {
5340 title: "Git Status Indicator",
5341 description: "Show a git status indicator next to file names in the project panel.",
5342 field: Box::new(SettingField {
5343 organization_override: None,
5344 json_path: Some("project_panel.git_status_indicator"),
5345 pick: |settings_content| {
5346 settings_content
5347 .project_panel
5348 .as_ref()?
5349 .git_status_indicator
5350 .as_ref()
5351 },
5352 write: |settings_content, value, _| {
5353 settings_content
5354 .project_panel
5355 .get_or_insert_default()
5356 .git_status_indicator = value;
5357 },
5358 }),
5359 metadata: None,
5360 files: USER,
5361 }),
5362 SettingsPageItem::SettingItem(SettingItem {
5363 title: "Sticky Scroll",
5364 description: "Whether to stick parent directories at top of the project panel.",
5365 field: Box::new(SettingField {
5366 organization_override: None,
5367 json_path: Some("project_panel.sticky_scroll"),
5368 pick: |settings_content| {
5369 settings_content
5370 .project_panel
5371 .as_ref()?
5372 .sticky_scroll
5373 .as_ref()
5374 },
5375 write: |settings_content, value, _| {
5376 settings_content
5377 .project_panel
5378 .get_or_insert_default()
5379 .sticky_scroll = value;
5380 },
5381 }),
5382 metadata: None,
5383 files: USER,
5384 }),
5385 SettingsPageItem::SettingItem(SettingItem {
5386 files: USER,
5387 title: "Show Indent Guides",
5388 description: "Show indent guides in the project panel.",
5389 field: Box::new(SettingField {
5390 organization_override: None,
5391 json_path: Some("project_panel.indent_guides.show"),
5392 pick: |settings_content| {
5393 settings_content
5394 .project_panel
5395 .as_ref()?
5396 .indent_guides
5397 .as_ref()?
5398 .show
5399 .as_ref()
5400 },
5401 write: |settings_content, value, _| {
5402 settings_content
5403 .project_panel
5404 .get_or_insert_default()
5405 .indent_guides
5406 .get_or_insert_default()
5407 .show = value;
5408 },
5409 }),
5410 metadata: None,
5411 }),
5412 SettingsPageItem::SettingItem(SettingItem {
5413 title: "Drag and Drop",
5414 description: "Whether to enable drag-and-drop operations in the project panel.",
5415 field: Box::new(SettingField {
5416 organization_override: None,
5417 json_path: Some("project_panel.drag_and_drop"),
5418 pick: |settings_content| {
5419 settings_content
5420 .project_panel
5421 .as_ref()?
5422 .drag_and_drop
5423 .as_ref()
5424 },
5425 write: |settings_content, value, _| {
5426 settings_content
5427 .project_panel
5428 .get_or_insert_default()
5429 .drag_and_drop = value;
5430 },
5431 }),
5432 metadata: None,
5433 files: USER,
5434 }),
5435 SettingsPageItem::SettingItem(SettingItem {
5436 title: "Hide Root",
5437 description: "Whether to hide the root entry when only one folder is open in the window.",
5438 field: Box::new(SettingField {
5439 organization_override: None,
5440 json_path: Some("project_panel.hide_root"),
5441 pick: |settings_content| {
5442 settings_content.project_panel.as_ref()?.hide_root.as_ref()
5443 },
5444 write: |settings_content, value, _| {
5445 settings_content
5446 .project_panel
5447 .get_or_insert_default()
5448 .hide_root = value;
5449 },
5450 }),
5451 metadata: None,
5452 files: USER,
5453 }),
5454 SettingsPageItem::SettingItem(SettingItem {
5455 title: "Hide Hidden",
5456 description: "Whether to hide the hidden entries in the project panel.",
5457 field: Box::new(SettingField {
5458 organization_override: None,
5459 json_path: Some("project_panel.hide_hidden"),
5460 pick: |settings_content| {
5461 settings_content
5462 .project_panel
5463 .as_ref()?
5464 .hide_hidden
5465 .as_ref()
5466 },
5467 write: |settings_content, value, _| {
5468 settings_content
5469 .project_panel
5470 .get_or_insert_default()
5471 .hide_hidden = value;
5472 },
5473 }),
5474 metadata: None,
5475 files: USER,
5476 }),
5477 SettingsPageItem::SettingItem(SettingItem {
5478 title: "Sort Mode",
5479 description: "Sort order for entries in the project panel.",
5480 field: Box::new(SettingField {
5481 organization_override: None,
5482 json_path: Some("project_panel.sort_mode"),
5483 pick: |settings_content| {
5484 settings_content.project_panel.as_ref()?.sort_mode.as_ref()
5485 },
5486 write: |settings_content, value, _| {
5487 settings_content
5488 .project_panel
5489 .get_or_insert_default()
5490 .sort_mode = value;
5491 },
5492 }),
5493 metadata: None,
5494 files: USER,
5495 }),
5496 SettingsPageItem::SettingItem(SettingItem {
5497 title: "Sort Order",
5498 description: "Whether to sort file and folder names case-sensitively in the project panel.",
5499 field: Box::new(SettingField {
5500 organization_override: None,
5501 pick: |settings_content| {
5502 settings_content.project_panel.as_ref()?.sort_order.as_ref()
5503 },
5504 write: |settings_content, value, _| {
5505 settings_content
5506 .project_panel
5507 .get_or_insert_default()
5508 .sort_order = value;
5509 },
5510 json_path: Some("project_panel.sort_order"),
5511 }),
5512 metadata: None,
5513 files: USER,
5514 }),
5515 SettingsPageItem::SettingItem(SettingItem {
5516 title: "Auto Open Files On Create",
5517 description: "Whether to automatically open newly created files in the editor.",
5518 field: Box::new(SettingField {
5519 organization_override: None,
5520 json_path: Some("project_panel.auto_open.on_create"),
5521 pick: |settings_content| {
5522 settings_content
5523 .project_panel
5524 .as_ref()?
5525 .auto_open
5526 .as_ref()?
5527 .on_create
5528 .as_ref()
5529 },
5530 write: |settings_content, value, _| {
5531 settings_content
5532 .project_panel
5533 .get_or_insert_default()
5534 .auto_open
5535 .get_or_insert_default()
5536 .on_create = value;
5537 },
5538 }),
5539 metadata: None,
5540 files: USER,
5541 }),
5542 SettingsPageItem::SettingItem(SettingItem {
5543 title: "Auto Open Files On Paste",
5544 description: "Whether to automatically open files after pasting or duplicating them.",
5545 field: Box::new(SettingField {
5546 organization_override: None,
5547 json_path: Some("project_panel.auto_open.on_paste"),
5548 pick: |settings_content| {
5549 settings_content
5550 .project_panel
5551 .as_ref()?
5552 .auto_open
5553 .as_ref()?
5554 .on_paste
5555 .as_ref()
5556 },
5557 write: |settings_content, value, _| {
5558 settings_content
5559 .project_panel
5560 .get_or_insert_default()
5561 .auto_open
5562 .get_or_insert_default()
5563 .on_paste = value;
5564 },
5565 }),
5566 metadata: None,
5567 files: USER,
5568 }),
5569 SettingsPageItem::SettingItem(SettingItem {
5570 title: "Auto Open Files On Drop",
5571 description: "Whether to automatically open files dropped from external sources.",
5572 field: Box::new(SettingField {
5573 organization_override: None,
5574 json_path: Some("project_panel.auto_open.on_drop"),
5575 pick: |settings_content| {
5576 settings_content
5577 .project_panel
5578 .as_ref()?
5579 .auto_open
5580 .as_ref()?
5581 .on_drop
5582 .as_ref()
5583 },
5584 write: |settings_content, value, _| {
5585 settings_content
5586 .project_panel
5587 .get_or_insert_default()
5588 .auto_open
5589 .get_or_insert_default()
5590 .on_drop = value;
5591 },
5592 }),
5593 metadata: None,
5594 files: USER,
5595 }),
5596 SettingsPageItem::SettingItem(SettingItem {
5597 title: "Hidden Files",
5598 description: "Globs to match files that will be considered \"hidden\" and can be hidden from the project panel.",
5599 field: Box::new(
5600 SettingField {
5601 organization_override: None,
5602 json_path: Some("worktree.hidden_files"),
5603 pick: |settings_content| {
5604 settings_content.project.worktree.hidden_files.as_ref()
5605 },
5606 write: |settings_content, value, _| {
5607 settings_content.project.worktree.hidden_files = value;
5608 },
5609 }
5610 .unimplemented(),
5611 ),
5612 metadata: None,
5613 files: USER,
5614 }),
5615 ]
5616 }
5617
5618 fn terminal_panel_section() -> [SettingsPageItem; 4] {
5619 [
5620 SettingsPageItem::SectionHeader("Terminal Panel"),
5621 SettingsPageItem::SettingItem(SettingItem {
5622 title: "Terminal Dock",
5623 description: "Where to dock the terminal panel.",
5624 field: Box::new(SettingField {
5625 organization_override: None,
5626 json_path: Some("terminal.dock"),
5627 pick: |settings_content| settings_content.terminal.as_ref()?.dock.as_ref(),
5628 write: |settings_content, value, _| {
5629 settings_content.terminal.get_or_insert_default().dock = value;
5630 },
5631 }),
5632 metadata: None,
5633 files: USER,
5634 }),
5635 SettingsPageItem::SettingItem(SettingItem {
5636 title: "Terminal Panel Flexible Sizing",
5637 description: "Whether the terminal panel should use flexible (proportional) sizing when docked to the left or right.",
5638 field: Box::new(SettingField {
5639 organization_override: None,
5640 json_path: Some("terminal.flexible"),
5641 pick: |settings_content| settings_content.terminal.as_ref()?.flexible.as_ref(),
5642 write: |settings_content, value, _| {
5643 settings_content.terminal.get_or_insert_default().flexible = value;
5644 },
5645 }),
5646 metadata: None,
5647 files: USER,
5648 }),
5649 SettingsPageItem::SettingItem(SettingItem {
5650 title: "Show Count Badge",
5651 description: "Show a badge on the terminal panel icon with the count of open terminals.",
5652 field: Box::new(SettingField {
5653 organization_override: None,
5654 json_path: Some("terminal.show_count_badge"),
5655 pick: |settings_content| {
5656 settings_content
5657 .terminal
5658 .as_ref()?
5659 .show_count_badge
5660 .as_ref()
5661 },
5662 write: |settings_content, value, _| {
5663 settings_content
5664 .terminal
5665 .get_or_insert_default()
5666 .show_count_badge = value;
5667 },
5668 }),
5669 metadata: None,
5670 files: USER,
5671 }),
5672 ]
5673 }
5674
5675 fn outline_panel_section() -> [SettingsPageItem; 11] {
5676 [
5677 SettingsPageItem::SectionHeader("Outline Panel"),
5678 SettingsPageItem::SettingItem(SettingItem {
5679 title: "Outline Panel Button",
5680 description: "Show the outline panel button in the status bar.",
5681 field: Box::new(SettingField {
5682 organization_override: None,
5683 json_path: Some("outline_panel.button"),
5684 pick: |settings_content| {
5685 settings_content.outline_panel.as_ref()?.button.as_ref()
5686 },
5687 write: |settings_content, value, _| {
5688 settings_content
5689 .outline_panel
5690 .get_or_insert_default()
5691 .button = value;
5692 },
5693 }),
5694 metadata: None,
5695 files: USER,
5696 }),
5697 SettingsPageItem::SettingItem(SettingItem {
5698 title: "Outline Panel Dock",
5699 description: "Where to dock the outline panel.",
5700 field: Box::new(SettingField {
5701 organization_override: None,
5702 json_path: Some("outline_panel.dock"),
5703 pick: |settings_content| settings_content.outline_panel.as_ref()?.dock.as_ref(),
5704 write: |settings_content, value, _| {
5705 settings_content.outline_panel.get_or_insert_default().dock = value;
5706 },
5707 }),
5708 metadata: None,
5709 files: USER,
5710 }),
5711 SettingsPageItem::SettingItem(SettingItem {
5712 title: "Outline Panel Default Width",
5713 description: "Default width of the outline panel in pixels.",
5714 field: Box::new(SettingField {
5715 organization_override: None,
5716 json_path: Some("outline_panel.default_width"),
5717 pick: |settings_content| {
5718 settings_content
5719 .outline_panel
5720 .as_ref()?
5721 .default_width
5722 .as_ref()
5723 },
5724 write: |settings_content, value, _| {
5725 settings_content
5726 .outline_panel
5727 .get_or_insert_default()
5728 .default_width = value;
5729 },
5730 }),
5731 metadata: None,
5732 files: USER,
5733 }),
5734 SettingsPageItem::SettingItem(SettingItem {
5735 title: "File Icons",
5736 description: "Show file icons in the outline panel.",
5737 field: Box::new(SettingField {
5738 organization_override: None,
5739 json_path: Some("outline_panel.file_icons"),
5740 pick: |settings_content| {
5741 settings_content.outline_panel.as_ref()?.file_icons.as_ref()
5742 },
5743 write: |settings_content, value, _| {
5744 settings_content
5745 .outline_panel
5746 .get_or_insert_default()
5747 .file_icons = value;
5748 },
5749 }),
5750 metadata: None,
5751 files: USER,
5752 }),
5753 SettingsPageItem::SettingItem(SettingItem {
5754 title: "Folder Icons",
5755 description: "Whether to show folder icons or chevrons for directories in the outline panel.",
5756 field: Box::new(SettingField {
5757 organization_override: None,
5758 json_path: Some("outline_panel.folder_icons"),
5759 pick: |settings_content| {
5760 settings_content
5761 .outline_panel
5762 .as_ref()?
5763 .folder_icons
5764 .as_ref()
5765 },
5766 write: |settings_content, value, _| {
5767 settings_content
5768 .outline_panel
5769 .get_or_insert_default()
5770 .folder_icons = value;
5771 },
5772 }),
5773 metadata: None,
5774 files: USER,
5775 }),
5776 SettingsPageItem::SettingItem(SettingItem {
5777 title: "Git Status",
5778 description: "Show the Git status in the outline panel.",
5779 field: Box::new(SettingField {
5780 organization_override: None,
5781 json_path: Some("outline_panel.git_status"),
5782 pick: |settings_content| {
5783 settings_content.outline_panel.as_ref()?.git_status.as_ref()
5784 },
5785 write: |settings_content, value, _| {
5786 settings_content
5787 .outline_panel
5788 .get_or_insert_default()
5789 .git_status = value;
5790 },
5791 }),
5792 metadata: None,
5793 files: USER,
5794 }),
5795 SettingsPageItem::SettingItem(SettingItem {
5796 title: "Indent Size",
5797 description: "Amount of indentation for nested items.",
5798 field: Box::new(SettingField {
5799 organization_override: None,
5800 json_path: Some("outline_panel.indent_size"),
5801 pick: |settings_content| {
5802 settings_content
5803 .outline_panel
5804 .as_ref()?
5805 .indent_size
5806 .as_ref()
5807 },
5808 write: |settings_content, value, _| {
5809 settings_content
5810 .outline_panel
5811 .get_or_insert_default()
5812 .indent_size = value;
5813 },
5814 }),
5815 metadata: None,
5816 files: USER,
5817 }),
5818 SettingsPageItem::SettingItem(SettingItem {
5819 title: "Auto Reveal Entries",
5820 description: "Whether to reveal when a corresponding outline entry becomes active.",
5821 field: Box::new(SettingField {
5822 organization_override: None,
5823 json_path: Some("outline_panel.auto_reveal_entries"),
5824 pick: |settings_content| {
5825 settings_content
5826 .outline_panel
5827 .as_ref()?
5828 .auto_reveal_entries
5829 .as_ref()
5830 },
5831 write: |settings_content, value, _| {
5832 settings_content
5833 .outline_panel
5834 .get_or_insert_default()
5835 .auto_reveal_entries = value;
5836 },
5837 }),
5838 metadata: None,
5839 files: USER,
5840 }),
5841 SettingsPageItem::SettingItem(SettingItem {
5842 title: "Auto Fold Directories",
5843 description: "Whether to fold directories automatically when a directory contains only one subdirectory.",
5844 field: Box::new(SettingField {
5845 organization_override: None,
5846 json_path: Some("outline_panel.auto_fold_dirs"),
5847 pick: |settings_content| {
5848 settings_content
5849 .outline_panel
5850 .as_ref()?
5851 .auto_fold_dirs
5852 .as_ref()
5853 },
5854 write: |settings_content, value, _| {
5855 settings_content
5856 .outline_panel
5857 .get_or_insert_default()
5858 .auto_fold_dirs = value;
5859 },
5860 }),
5861 metadata: None,
5862 files: USER,
5863 }),
5864 SettingsPageItem::SettingItem(SettingItem {
5865 files: USER,
5866 title: "Show Indent Guides",
5867 description: "When to show indent guides in the outline panel.",
5868 field: Box::new(SettingField {
5869 organization_override: None,
5870 json_path: Some("outline_panel.indent_guides.show"),
5871 pick: |settings_content| {
5872 settings_content
5873 .outline_panel
5874 .as_ref()?
5875 .indent_guides
5876 .as_ref()?
5877 .show
5878 .as_ref()
5879 },
5880 write: |settings_content, value, _| {
5881 settings_content
5882 .outline_panel
5883 .get_or_insert_default()
5884 .indent_guides
5885 .get_or_insert_default()
5886 .show = value;
5887 },
5888 }),
5889 metadata: None,
5890 }),
5891 ]
5892 }
5893
5894 fn git_panel_section() -> [SettingsPageItem; 17] {
5895 [
5896 SettingsPageItem::SectionHeader("Git Panel"),
5897 SettingsPageItem::SettingItem(SettingItem {
5898 title: "Git Panel Button",
5899 description: "Show the Git panel button in the status bar.",
5900 field: Box::new(SettingField {
5901 organization_override: None,
5902 json_path: Some("git_panel.button"),
5903 pick: |settings_content| settings_content.git_panel.as_ref()?.button.as_ref(),
5904 write: |settings_content, value, _| {
5905 settings_content.git_panel.get_or_insert_default().button = value;
5906 },
5907 }),
5908 metadata: None,
5909 files: USER,
5910 }),
5911 SettingsPageItem::SettingItem(SettingItem {
5912 title: "Git Panel Dock",
5913 description: "Where to dock the Git panel.",
5914 field: Box::new(SettingField {
5915 organization_override: None,
5916 json_path: Some("git_panel.dock"),
5917 pick: |settings_content| settings_content.git_panel.as_ref()?.dock.as_ref(),
5918 write: |settings_content, value, _| {
5919 settings_content.git_panel.get_or_insert_default().dock = value;
5920 },
5921 }),
5922 metadata: None,
5923 files: USER,
5924 }),
5925 SettingsPageItem::SettingItem(SettingItem {
5926 title: "Git Panel Default Width",
5927 description: "Default width of the Git panel in pixels.",
5928 field: Box::new(SettingField {
5929 organization_override: None,
5930 json_path: Some("git_panel.default_width"),
5931 pick: |settings_content| {
5932 settings_content.git_panel.as_ref()?.default_width.as_ref()
5933 },
5934 write: |settings_content, value, _| {
5935 settings_content
5936 .git_panel
5937 .get_or_insert_default()
5938 .default_width = value;
5939 },
5940 }),
5941 metadata: None,
5942 files: USER,
5943 }),
5944 SettingsPageItem::SettingItem(SettingItem {
5945 title: "Git Panel Status Style",
5946 description: "How entry statuses are displayed.",
5947 field: Box::new(SettingField {
5948 organization_override: None,
5949 json_path: Some("git_panel.status_style"),
5950 pick: |settings_content| {
5951 settings_content.git_panel.as_ref()?.status_style.as_ref()
5952 },
5953 write: |settings_content, value, _| {
5954 settings_content
5955 .git_panel
5956 .get_or_insert_default()
5957 .status_style = value;
5958 },
5959 }),
5960 metadata: None,
5961 files: USER,
5962 }),
5963 SettingsPageItem::SettingItem(SettingItem {
5964 title: "Fallback Branch Name",
5965 description: "Default branch name will be when init.defaultbranch is not set in Git.",
5966 field: Box::new(SettingField {
5967 organization_override: None,
5968 json_path: Some("git_panel.fallback_branch_name"),
5969 pick: |settings_content| {
5970 settings_content
5971 .git_panel
5972 .as_ref()?
5973 .fallback_branch_name
5974 .as_ref()
5975 },
5976 write: |settings_content, value, _| {
5977 settings_content
5978 .git_panel
5979 .get_or_insert_default()
5980 .fallback_branch_name = value;
5981 },
5982 }),
5983 metadata: None,
5984 files: USER,
5985 }),
5986 SettingsPageItem::SettingItem(SettingItem {
5987 title: "Sort By",
5988 description: "How to sort entries in the git panel.",
5989 field: Box::new(SettingField {
5990 organization_override: None,
5991 json_path: Some("git_panel.sort_by"),
5992 pick: |settings_content| settings_content.git_panel.as_ref()?.sort_by.as_ref(),
5993 write: |settings_content, value, _| {
5994 settings_content.git_panel.get_or_insert_default().sort_by = value;
5995 },
5996 }),
5997 metadata: None,
5998 files: USER,
5999 }),
6000 SettingsPageItem::SettingItem(SettingItem {
6001 title: "Group By",
6002 description: "How to group entries in the git panel.",
6003 field: Box::new(SettingField {
6004 organization_override: None,
6005 json_path: Some("git_panel.group_by"),
6006 pick: |settings_content| settings_content.git_panel.as_ref()?.group_by.as_ref(),
6007 write: |settings_content, value, _| {
6008 settings_content.git_panel.get_or_insert_default().group_by = value;
6009 },
6010 }),
6011 metadata: None,
6012 files: USER,
6013 }),
6014 SettingsPageItem::SettingItem(SettingItem {
6015 title: "Collapse Untracked Diff",
6016 description: "Whether to collapse untracked files in the diff panel.",
6017 field: Box::new(SettingField {
6018 organization_override: None,
6019 json_path: Some("git_panel.collapse_untracked_diff"),
6020 pick: |settings_content| {
6021 settings_content
6022 .git_panel
6023 .as_ref()?
6024 .collapse_untracked_diff
6025 .as_ref()
6026 },
6027 write: |settings_content, value, _| {
6028 settings_content
6029 .git_panel
6030 .get_or_insert_default()
6031 .collapse_untracked_diff = value;
6032 },
6033 }),
6034 metadata: None,
6035 files: USER,
6036 }),
6037 SettingsPageItem::SettingItem(SettingItem {
6038 title: "Tree View",
6039 description: "Enable to show entries in tree view list, disable to show in flat view list.",
6040 field: Box::new(SettingField {
6041 organization_override: None,
6042 json_path: Some("git_panel.tree_view"),
6043 pick: |settings_content| {
6044 settings_content.git_panel.as_ref()?.tree_view.as_ref()
6045 },
6046 write: |settings_content, value, _| {
6047 settings_content.git_panel.get_or_insert_default().tree_view = value;
6048 },
6049 }),
6050 metadata: None,
6051 files: USER,
6052 }),
6053 SettingsPageItem::SettingItem(SettingItem {
6054 title: "File Icons",
6055 description: "Show file icons next to the Git status icon.",
6056 field: Box::new(SettingField {
6057 organization_override: None,
6058 json_path: Some("git_panel.file_icons"),
6059 pick: |settings_content| {
6060 settings_content.git_panel.as_ref()?.file_icons.as_ref()
6061 },
6062 write: |settings_content, value, _| {
6063 settings_content
6064 .git_panel
6065 .get_or_insert_default()
6066 .file_icons = value;
6067 },
6068 }),
6069 metadata: None,
6070 files: USER,
6071 }),
6072 SettingsPageItem::SettingItem(SettingItem {
6073 title: "Folder Icons",
6074 description: "Whether to show folder icons or chevrons for directories in the git panel.",
6075 field: Box::new(SettingField {
6076 organization_override: None,
6077 json_path: Some("git_panel.folder_icons"),
6078 pick: |settings_content| {
6079 settings_content.git_panel.as_ref()?.folder_icons.as_ref()
6080 },
6081 write: |settings_content, value, _| {
6082 settings_content
6083 .git_panel
6084 .get_or_insert_default()
6085 .folder_icons = value;
6086 },
6087 }),
6088 metadata: None,
6089 files: USER,
6090 }),
6091 SettingsPageItem::SettingItem(SettingItem {
6092 title: "Diff Stats",
6093 description: "Whether to show the addition/deletion change count next to each file in the Git panel.",
6094 field: Box::new(SettingField {
6095 organization_override: None,
6096 json_path: Some("git_panel.diff_stats"),
6097 pick: |settings_content| {
6098 settings_content.git_panel.as_ref()?.diff_stats.as_ref()
6099 },
6100 write: |settings_content, value, _| {
6101 settings_content
6102 .git_panel
6103 .get_or_insert_default()
6104 .diff_stats = value;
6105 },
6106 }),
6107 metadata: None,
6108 files: USER,
6109 }),
6110 SettingsPageItem::SettingItem(SettingItem {
6111 title: "Primary Click Behavior",
6112 description: "Default action when clicking a changed file in the Git panel.",
6113 field: Box::new(SettingField {
6114 organization_override: None,
6115 json_path: Some("git_panel.entry_primary_click_action"),
6116 pick: |settings_content| {
6117 settings_content
6118 .git_panel
6119 .as_ref()?
6120 .entry_primary_click_action
6121 .as_ref()
6122 },
6123 write: |settings_content, value, _| {
6124 settings_content
6125 .git_panel
6126 .get_or_insert_default()
6127 .entry_primary_click_action = value;
6128 },
6129 }),
6130 metadata: None,
6131 files: USER,
6132 }),
6133 SettingsPageItem::SettingItem(SettingItem {
6134 title: "Show Count Badge",
6135 description: "Whether to show a badge on the git panel icon with the count of uncommitted changes.",
6136 field: Box::new(SettingField {
6137 organization_override: None,
6138 json_path: Some("git_panel.show_count_badge"),
6139 pick: |settings_content| {
6140 settings_content
6141 .git_panel
6142 .as_ref()?
6143 .show_count_badge
6144 .as_ref()
6145 },
6146 write: |settings_content, value, _| {
6147 settings_content
6148 .git_panel
6149 .get_or_insert_default()
6150 .show_count_badge = value;
6151 },
6152 }),
6153 metadata: None,
6154 files: USER,
6155 }),
6156 SettingsPageItem::SettingItem(SettingItem {
6157 title: "Commit Title Max Length",
6158 description: "Maximum length of the commit message title before a warning is shown. Set to 0 to disable.",
6159 field: Box::new(SettingField {
6160 organization_override: None,
6161 json_path: Some("git_panel.commit_title_max_length"),
6162 pick: |settings_content| {
6163 settings_content
6164 .git_panel
6165 .as_ref()?
6166 .commit_title_max_length
6167 .as_ref()
6168 },
6169 write: |settings_content, value, _| {
6170 settings_content
6171 .git_panel
6172 .get_or_insert_default()
6173 .commit_title_max_length = value;
6174 },
6175 }),
6176 metadata: None,
6177 files: USER,
6178 }),
6179 SettingsPageItem::SettingItem(SettingItem {
6180 title: "Scroll Bar",
6181 description: "How and when the scrollbar should be displayed.",
6182 field: Box::new(SettingField {
6183 organization_override: None,
6184 json_path: Some("git_panel.scrollbar.show"),
6185 pick: |settings_content| {
6186 show_scrollbar_or_editor(settings_content, |settings_content| {
6187 settings_content
6188 .git_panel
6189 .as_ref()?
6190 .scrollbar
6191 .as_ref()?
6192 .show
6193 .as_ref()
6194 })
6195 },
6196 write: |settings_content, value, _| {
6197 settings_content
6198 .git_panel
6199 .get_or_insert_default()
6200 .scrollbar
6201 .get_or_insert_default()
6202 .show = value;
6203 },
6204 }),
6205 metadata: None,
6206 files: USER,
6207 }),
6208 ]
6209 }
6210
6211 fn debugger_panel_section() -> [SettingsPageItem; 2] {
6212 [
6213 SettingsPageItem::SectionHeader("Debugger Panel"),
6214 SettingsPageItem::SettingItem(SettingItem {
6215 title: "Debugger Panel Dock",
6216 description: "The dock position of the debug panel.",
6217 field: Box::new(SettingField {
6218 organization_override: None,
6219 json_path: Some("debugger.dock"),
6220 pick: |settings_content| settings_content.debugger.as_ref()?.dock.as_ref(),
6221 write: |settings_content, value, _| {
6222 settings_content.debugger.get_or_insert_default().dock = value;
6223 },
6224 }),
6225 metadata: None,
6226 files: USER,
6227 }),
6228 ]
6229 }
6230
6231 fn collaboration_panel_section() -> [SettingsPageItem; 4] {
6232 [
6233 SettingsPageItem::SectionHeader("Collaboration Panel"),
6234 SettingsPageItem::SettingItem(SettingItem {
6235 title: "Collaboration Panel Button",
6236 description: "Show the collaboration panel button in the status bar.",
6237 field: Box::new(SettingField {
6238 organization_override: None,
6239 json_path: Some("collaboration_panel.button"),
6240 pick: |settings_content| {
6241 settings_content
6242 .collaboration_panel
6243 .as_ref()?
6244 .button
6245 .as_ref()
6246 },
6247 write: |settings_content, value, _| {
6248 settings_content
6249 .collaboration_panel
6250 .get_or_insert_default()
6251 .button = value;
6252 },
6253 }),
6254 metadata: None,
6255 files: USER,
6256 }),
6257 SettingsPageItem::SettingItem(SettingItem {
6258 title: "Collaboration Panel Dock",
6259 description: "Where to dock the collaboration panel.",
6260 field: Box::new(SettingField {
6261 organization_override: None,
6262 json_path: Some("collaboration_panel.dock"),
6263 pick: |settings_content| {
6264 settings_content.collaboration_panel.as_ref()?.dock.as_ref()
6265 },
6266 write: |settings_content, value, _| {
6267 settings_content
6268 .collaboration_panel
6269 .get_or_insert_default()
6270 .dock = value;
6271 },
6272 }),
6273 metadata: None,
6274 files: USER,
6275 }),
6276 SettingsPageItem::SettingItem(SettingItem {
6277 title: "Collaboration Panel Default Width",
6278 description: "Default width of the collaboration panel in pixels.",
6279 field: Box::new(SettingField {
6280 organization_override: None,
6281 json_path: Some("collaboration_panel.dock"),
6282 pick: |settings_content| {
6283 settings_content
6284 .collaboration_panel
6285 .as_ref()?
6286 .default_width
6287 .as_ref()
6288 },
6289 write: |settings_content, value, _| {
6290 settings_content
6291 .collaboration_panel
6292 .get_or_insert_default()
6293 .default_width = value;
6294 },
6295 }),
6296 metadata: None,
6297 files: USER,
6298 }),
6299 ]
6300 }
6301
6302 fn agent_panel_section() -> [SettingsPageItem; 7] {
6303 [
6304 SettingsPageItem::SectionHeader("Agent Panel"),
6305 SettingsPageItem::SettingItem(SettingItem {
6306 title: "Agent Panel Button",
6307 description: "Whether to show the agent panel button in the status bar.",
6308 field: Box::new(SettingField {
6309 organization_override: None,
6310 json_path: Some("agent.button"),
6311 pick: |settings_content| settings_content.agent.as_ref()?.button.as_ref(),
6312 write: |settings_content, value, _| {
6313 settings_content.agent.get_or_insert_default().button = value;
6314 },
6315 }),
6316 metadata: None,
6317 files: USER,
6318 }),
6319 SettingsPageItem::SettingItem(SettingItem {
6320 title: "Agent Panel Dock",
6321 description: "Where to dock the agent panel.",
6322 field: Box::new(SettingField {
6323 organization_override: None,
6324 json_path: Some("agent.dock"),
6325 pick: |settings_content| settings_content.agent.as_ref()?.dock.as_ref(),
6326 write: |settings_content, value, _| {
6327 settings_content.agent.get_or_insert_default().dock = value;
6328 },
6329 }),
6330 metadata: None,
6331 files: USER,
6332 }),
6333 SettingsPageItem::SettingItem(SettingItem {
6334 title: "Agent Panel Flexible Sizing",
6335 description: "Whether the agent panel should use flexible (proportional) sizing when docked to the left or right.",
6336 field: Box::new(SettingField {
6337 organization_override: None,
6338 json_path: Some("agent.flexible"),
6339 pick: |settings_content| settings_content.agent.as_ref()?.flexible.as_ref(),
6340 write: |settings_content, value, _| {
6341 settings_content.agent.get_or_insert_default().flexible = value;
6342 },
6343 }),
6344 metadata: None,
6345 files: USER,
6346 }),
6347 SettingsPageItem::SettingItem(SettingItem {
6348 title: "Agent Panel Default Width",
6349 description: "Default width when the agent panel is docked to the left or right.",
6350 field: Box::new(SettingField {
6351 organization_override: None,
6352 json_path: Some("agent.default_width"),
6353 pick: |settings_content| {
6354 settings_content.agent.as_ref()?.default_width.as_ref()
6355 },
6356 write: |settings_content, value, _| {
6357 settings_content.agent.get_or_insert_default().default_width = value;
6358 },
6359 }),
6360 metadata: None,
6361 files: USER,
6362 }),
6363 SettingsPageItem::SettingItem(SettingItem {
6364 title: "Agent Panel Default Height",
6365 description: "Default height when the agent panel is docked to the bottom.",
6366 field: Box::new(SettingField {
6367 organization_override: None,
6368 json_path: Some("agent.default_height"),
6369 pick: |settings_content| {
6370 settings_content.agent.as_ref()?.default_height.as_ref()
6371 },
6372 write: |settings_content, value, _| {
6373 settings_content
6374 .agent
6375 .get_or_insert_default()
6376 .default_height = value;
6377 },
6378 }),
6379 metadata: None,
6380 files: USER,
6381 }),
6382 SettingsPageItem::DynamicItem(DynamicItem {
6383 discriminant: SettingItem {
6384 files: USER,
6385 title: "Limit Content Width",
6386 description: "Whether to constrain the agent panel content to a maximum width, centering it when the panel is wider, for optimal readability.",
6387 field: Box::new(SettingField::<bool> {
6388 organization_override: None,
6389 json_path: Some("agent.limit_content_width"),
6390 pick: |settings_content| {
6391 settings_content
6392 .agent
6393 .as_ref()?
6394 .limit_content_width
6395 .as_ref()
6396 },
6397 write: |settings_content, value, _| {
6398 settings_content
6399 .agent
6400 .get_or_insert_default()
6401 .limit_content_width = value;
6402 },
6403 }),
6404 metadata: None,
6405 },
6406 pick_discriminant: |settings_content| {
6407 let enabled = settings_content
6408 .agent
6409 .as_ref()?
6410 .limit_content_width
6411 .unwrap_or(true);
6412 Some(if enabled { 1 } else { 0 })
6413 },
6414 fields: vec![
6415 vec![],
6416 vec![SettingItem {
6417 files: USER,
6418 title: "Max Content Width",
6419 description: "Maximum content width in pixels. Content will be centered when the panel is wider than this value.",
6420 field: Box::new(SettingField {
6421 organization_override: None,
6422 json_path: Some("agent.max_content_width"),
6423 pick: |settings_content| {
6424 settings_content.agent.as_ref()?.max_content_width.as_ref()
6425 },
6426 write: |settings_content, value, _| {
6427 settings_content
6428 .agent
6429 .get_or_insert_default()
6430 .max_content_width = value;
6431 },
6432 }),
6433 metadata: None,
6434 }],
6435 ],
6436 }),
6437 ]
6438 }
6439
6440 SettingsPage {
6441 title: "Panels",
6442 items: concat_sections![
6443 project_panel_section(),
6444 terminal_panel_section(),
6445 outline_panel_section(),
6446 git_panel_section(),
6447 debugger_panel_section(),
6448 collaboration_panel_section(),
6449 agent_panel_section(),
6450 ],
6451 }
6452}
6453
6454fn debugger_page() -> SettingsPage {
6455 fn general_section() -> [SettingsPageItem; 6] {
6456 [
6457 SettingsPageItem::SectionHeader("General"),
6458 SettingsPageItem::SettingItem(SettingItem {
6459 title: "Stepping Granularity",
6460 description: "Determines the stepping granularity for debug operations.",
6461 field: Box::new(SettingField {
6462 organization_override: None,
6463 json_path: Some("debugger.stepping_granularity"),
6464 pick: |settings_content| {
6465 settings_content
6466 .debugger
6467 .as_ref()?
6468 .stepping_granularity
6469 .as_ref()
6470 },
6471 write: |settings_content, value, _| {
6472 settings_content
6473 .debugger
6474 .get_or_insert_default()
6475 .stepping_granularity = value;
6476 },
6477 }),
6478 metadata: None,
6479 files: USER,
6480 }),
6481 SettingsPageItem::SettingItem(SettingItem {
6482 title: "Save Breakpoints",
6483 description: "Whether breakpoints should be reused across Omega sessions.",
6484 field: Box::new(SettingField {
6485 organization_override: None,
6486 json_path: Some("debugger.save_breakpoints"),
6487 pick: |settings_content| {
6488 settings_content
6489 .debugger
6490 .as_ref()?
6491 .save_breakpoints
6492 .as_ref()
6493 },
6494 write: |settings_content, value, _| {
6495 settings_content
6496 .debugger
6497 .get_or_insert_default()
6498 .save_breakpoints = value;
6499 },
6500 }),
6501 metadata: None,
6502 files: USER,
6503 }),
6504 SettingsPageItem::SettingItem(SettingItem {
6505 title: "Timeout",
6506 description: "Time in milliseconds until timeout error when connecting to a TCP debug adapter.",
6507 field: Box::new(SettingField {
6508 organization_override: None,
6509 json_path: Some("debugger.timeout"),
6510 pick: |settings_content| settings_content.debugger.as_ref()?.timeout.as_ref(),
6511 write: |settings_content, value, _| {
6512 settings_content.debugger.get_or_insert_default().timeout = value;
6513 },
6514 }),
6515 metadata: None,
6516 files: USER,
6517 }),
6518 SettingsPageItem::SettingItem(SettingItem {
6519 title: "Log DAP Communications",
6520 description: "Whether to log messages between active debug adapters and Omega.",
6521 field: Box::new(SettingField {
6522 organization_override: None,
6523 json_path: Some("debugger.log_dap_communications"),
6524 pick: |settings_content| {
6525 settings_content
6526 .debugger
6527 .as_ref()?
6528 .log_dap_communications
6529 .as_ref()
6530 },
6531 write: |settings_content, value, _| {
6532 settings_content
6533 .debugger
6534 .get_or_insert_default()
6535 .log_dap_communications = value;
6536 },
6537 }),
6538 metadata: None,
6539 files: USER,
6540 }),
6541 SettingsPageItem::SettingItem(SettingItem {
6542 title: "Format DAP Log Messages",
6543 description: "Whether to format DAP messages when adding them to debug adapter logger.",
6544 field: Box::new(SettingField {
6545 organization_override: None,
6546 json_path: Some("debugger.format_dap_log_messages"),
6547 pick: |settings_content| {
6548 settings_content
6549 .debugger
6550 .as_ref()?
6551 .format_dap_log_messages
6552 .as_ref()
6553 },
6554 write: |settings_content, value, _| {
6555 settings_content
6556 .debugger
6557 .get_or_insert_default()
6558 .format_dap_log_messages = value;
6559 },
6560 }),
6561 metadata: None,
6562 files: USER,
6563 }),
6564 ]
6565 }
6566
6567 SettingsPage {
6568 title: "Debugger",
6569 items: concat_sections![general_section()],
6570 }
6571}
6572
6573fn terminal_page() -> SettingsPage {
6574 fn environment_section() -> [SettingsPageItem; 5] {
6575 [
6576 SettingsPageItem::SectionHeader("Environment"),
6577 SettingsPageItem::DynamicItem(DynamicItem {
6578 discriminant: SettingItem {
6579 files: USER | PROJECT,
6580 title: "Shell",
6581 description: "What shell to use when opening a terminal.",
6582 field: Box::new(SettingField {
6583 organization_override: None,
6584 json_path: Some("terminal.shell$"),
6585 pick: |settings_content| {
6586 Some(&dynamic_variants::<settings::Shell>()[
6587 settings_content
6588 .terminal
6589 .as_ref()?
6590 .project
6591 .shell
6592 .as_ref()?
6593 .discriminant() as usize
6594 ])
6595 },
6596 write: |settings_content, value, _| {
6597 let Some(value) = value else {
6598 if let Some(terminal) = settings_content.terminal.as_mut() {
6599 terminal.project.shell = None;
6600 }
6601 return;
6602 };
6603 let settings_value = settings_content
6604 .terminal
6605 .get_or_insert_default()
6606 .project
6607 .shell
6608 .get_or_insert_with(|| settings::Shell::default());
6609 let default_shell = if cfg!(target_os = "windows") {
6610 "powershell.exe"
6611 } else {
6612 "sh"
6613 };
6614 *settings_value = match value {
6615 settings::ShellDiscriminants::System => settings::Shell::System,
6616 settings::ShellDiscriminants::Program => {
6617 let program = match settings_value {
6618 settings::Shell::Program(program) => program.clone(),
6619 settings::Shell::WithArguments { program, .. } => program.clone(),
6620 _ => String::from(default_shell),
6621 };
6622 settings::Shell::Program(program)
6623 }
6624 settings::ShellDiscriminants::WithArguments => {
6625 let (program, args, title_override) = match settings_value {
6626 settings::Shell::Program(program) => (program.clone(), vec![], None),
6627 settings::Shell::WithArguments {
6628 program,
6629 args,
6630 title_override,
6631 } => (program.clone(), args.clone(), title_override.clone()),
6632 _ => (String::from(default_shell), vec![], None),
6633 };
6634 settings::Shell::WithArguments {
6635 program,
6636 args,
6637 title_override,
6638 }
6639 }
6640 };
6641 },
6642 }),
6643 metadata: None,
6644 },
6645 pick_discriminant: |settings_content| {
6646 Some(
6647 settings_content
6648 .terminal
6649 .as_ref()?
6650 .project
6651 .shell
6652 .as_ref()?
6653 .discriminant() as usize,
6654 )
6655 },
6656 fields: dynamic_variants::<settings::Shell>()
6657 .into_iter()
6658 .map(|variant| match variant {
6659 settings::ShellDiscriminants::System => vec![],
6660 settings::ShellDiscriminants::Program => vec![SettingItem {
6661 files: USER | PROJECT,
6662 title: "Program",
6663 description: "The shell program to use.",
6664 field: Box::new(SettingField {
6665 organization_override: None,
6666 json_path: Some("terminal.shell"),
6667 pick: |settings_content| match settings_content.terminal.as_ref()?.project.shell.as_ref()
6668 {
6669 Some(settings::Shell::Program(program)) => Some(program),
6670 _ => None,
6671 },
6672 write: |settings_content, value, _| {
6673 let Some(value) = value else {
6674 return;
6675 };
6676 match settings_content
6677 .terminal
6678 .get_or_insert_default()
6679 .project
6680 .shell
6681 .as_mut()
6682 {
6683 Some(settings::Shell::Program(program)) => *program = value,
6684 _ => return,
6685 }
6686 },
6687 }),
6688 metadata: None,
6689 }],
6690 settings::ShellDiscriminants::WithArguments => vec![
6691 SettingItem {
6692 files: USER | PROJECT,
6693 title: "Program",
6694 description: "The shell program to run.",
6695 field: Box::new(SettingField {
6696 organization_override: None,
6697 json_path: Some("terminal.shell.program"),
6698 pick: |settings_content| {
6699 match settings_content.terminal.as_ref()?.project.shell.as_ref() {
6700 Some(settings::Shell::WithArguments { program, .. }) => Some(program),
6701 _ => None,
6702 }
6703 },
6704 write: |settings_content, value, _| {
6705 let Some(value) = value else {
6706 return;
6707 };
6708 match settings_content
6709 .terminal
6710 .get_or_insert_default()
6711 .project
6712 .shell
6713 .as_mut()
6714 {
6715 Some(settings::Shell::WithArguments { program, .. }) => {
6716 *program = value
6717 }
6718 _ => return,
6719 }
6720 },
6721 }),
6722 metadata: None,
6723 },
6724 SettingItem {
6725 files: USER | PROJECT,
6726 title: "Arguments",
6727 description: "The arguments to pass to the shell program.",
6728 field: Box::new(
6729 SettingField {
6730 organization_override: None,
6731 json_path: Some("terminal.shell.args"),
6732 pick: |settings_content| {
6733 match settings_content.terminal.as_ref()?.project.shell.as_ref() {
6734 Some(settings::Shell::WithArguments { args, .. }) => Some(args),
6735 _ => None,
6736 }
6737 },
6738 write: |settings_content, value, _| {
6739 let Some(value) = value else {
6740 return;
6741 };
6742 match settings_content
6743 .terminal
6744 .get_or_insert_default()
6745 .project
6746 .shell
6747 .as_mut()
6748 {
6749 Some(settings::Shell::WithArguments { args, .. }) => *args = value,
6750 _ => return,
6751 }
6752 },
6753 }
6754 .unimplemented(),
6755 ),
6756 metadata: None,
6757 },
6758 SettingItem {
6759 files: USER | PROJECT,
6760 title: "Title Override",
6761 description: "An optional string to override the title of the terminal tab.",
6762 field: Box::new(SettingField {
6763 organization_override: None,
6764 json_path: Some("terminal.shell.title_override"),
6765 pick: |settings_content| {
6766 match settings_content.terminal.as_ref()?.project.shell.as_ref() {
6767 Some(settings::Shell::WithArguments { title_override, .. }) => {
6768 title_override.as_ref().or(DEFAULT_EMPTY_STRING)
6769 }
6770 _ => None,
6771 }
6772 },
6773 write: |settings_content, value, _| {
6774 match settings_content
6775 .terminal
6776 .get_or_insert_default()
6777 .project
6778 .shell
6779 .as_mut()
6780 {
6781 Some(settings::Shell::WithArguments { title_override, .. }) => {
6782 *title_override = value.filter(|s| !s.is_empty())
6783 }
6784 _ => return,
6785 }
6786 },
6787 }),
6788 metadata: None,
6789 },
6790 ],
6791 })
6792 .collect(),
6793 }),
6794 SettingsPageItem::DynamicItem(DynamicItem {
6795 discriminant: SettingItem {
6796 files: USER | PROJECT,
6797 title: "Working Directory",
6798 description: "What working directory to use when launching the terminal.",
6799 field: Box::new(SettingField {
6800 organization_override: None,
6801 json_path: Some("terminal.working_directory$"),
6802 pick: |settings_content| {
6803 Some(&dynamic_variants::<settings::WorkingDirectory>()[
6804 settings_content
6805 .terminal
6806 .as_ref()?
6807 .project
6808 .working_directory
6809 .as_ref()?
6810 .discriminant() as usize
6811 ])
6812 },
6813 write: |settings_content, value, _| {
6814 let Some(value) = value else {
6815 if let Some(terminal) = settings_content.terminal.as_mut() {
6816 terminal.project.working_directory = None;
6817 }
6818 return;
6819 };
6820 let settings_value = settings_content
6821 .terminal
6822 .get_or_insert_default()
6823 .project
6824 .working_directory
6825 .get_or_insert_with(|| settings::WorkingDirectory::CurrentProjectDirectory);
6826 *settings_value = match value {
6827 settings::WorkingDirectoryDiscriminants::CurrentFileDirectory => {
6828 settings::WorkingDirectory::CurrentFileDirectory
6829 },
6830 settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => {
6831 settings::WorkingDirectory::CurrentProjectDirectory
6832 }
6833 settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => {
6834 settings::WorkingDirectory::FirstProjectDirectory
6835 }
6836 settings::WorkingDirectoryDiscriminants::AlwaysHome => {
6837 settings::WorkingDirectory::AlwaysHome
6838 }
6839 settings::WorkingDirectoryDiscriminants::Always => {
6840 let directory = match settings_value {
6841 settings::WorkingDirectory::Always { .. } => return,
6842 _ => String::new(),
6843 };
6844 settings::WorkingDirectory::Always { directory }
6845 }
6846 };
6847 },
6848 }),
6849 metadata: None,
6850 },
6851 pick_discriminant: |settings_content| {
6852 Some(
6853 settings_content
6854 .terminal
6855 .as_ref()?
6856 .project
6857 .working_directory
6858 .as_ref()?
6859 .discriminant() as usize,
6860 )
6861 },
6862 fields: dynamic_variants::<settings::WorkingDirectory>()
6863 .into_iter()
6864 .map(|variant| match variant {
6865 settings::WorkingDirectoryDiscriminants::CurrentFileDirectory => vec![],
6866 settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => vec![],
6867 settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => vec![],
6868 settings::WorkingDirectoryDiscriminants::AlwaysHome => vec![],
6869 settings::WorkingDirectoryDiscriminants::Always => vec![SettingItem {
6870 files: USER | PROJECT,
6871 title: "Directory",
6872 description: "The directory path to use (will be shell expanded).",
6873 field: Box::new(SettingField {
6874 organization_override: None,
6875 json_path: Some("terminal.working_directory.always"),
6876 pick: |settings_content| {
6877 match settings_content.terminal.as_ref()?.project.working_directory.as_ref() {
6878 Some(settings::WorkingDirectory::Always { directory }) => Some(directory),
6879 _ => None,
6880 }
6881 },
6882 write: |settings_content, value, _| {
6883 let value = value.unwrap_or_default();
6884 match settings_content
6885 .terminal
6886 .get_or_insert_default()
6887 .project
6888 .working_directory
6889 .as_mut()
6890 {
6891 Some(settings::WorkingDirectory::Always { directory }) => *directory = value,
6892 _ => return,
6893 }
6894 },
6895 }),
6896 metadata: None,
6897 }],
6898 })
6899 .collect(),
6900 }),
6901 SettingsPageItem::SettingItem(SettingItem {
6902 title: "Environment Variables",
6903 description: "Key-value pairs to add to the terminal's environment.",
6904 field: Box::new(
6905 SettingField {
6906 organization_override: None,
6907 json_path: Some("terminal.env"),
6908 pick: |settings_content| settings_content.terminal.as_ref()?.project.env.as_ref(),
6909 write: |settings_content, value, _| {
6910 settings_content.terminal.get_or_insert_default().project.env = value;
6911 },
6912 }
6913 .unimplemented(),
6914 ),
6915 metadata: None,
6916 files: USER | PROJECT,
6917 }),
6918 SettingsPageItem::SettingItem(SettingItem {
6919 title: "Detect Virtual Environment",
6920 description: "Activates the Python virtual environment, if one is found, in the terminal's working directory.",
6921 field: Box::new(
6922 SettingField {
6923 organization_override: None,
6924 json_path: Some("terminal.detect_venv"),
6925 pick: |settings_content| settings_content.terminal.as_ref()?.project.detect_venv.as_ref(),
6926 write: |settings_content, value, _| {
6927 settings_content
6928 .terminal
6929 .get_or_insert_default()
6930 .project
6931 .detect_venv = value;
6932 },
6933 }
6934 .unimplemented(),
6935 ),
6936 metadata: None,
6937 files: USER | PROJECT,
6938 }),
6939 ]
6940 }
6941
6942 fn font_section() -> [SettingsPageItem; 6] {
6943 [
6944 SettingsPageItem::SectionHeader("Font"),
6945 SettingsPageItem::SettingItem(SettingItem {
6946 title: "Font Size",
6947 description: "Font size for terminal text. If not set, defaults to buffer font size.",
6948 field: Box::new(SettingField {
6949 organization_override: None,
6950 json_path: Some("terminal.font_size"),
6951 pick: |settings_content| {
6952 settings_content
6953 .terminal
6954 .as_ref()
6955 .and_then(|terminal| terminal.font_size.as_ref())
6956 .or(settings_content.theme.buffer_font_size.as_ref())
6957 },
6958 write: |settings_content, value, _| {
6959 settings_content.terminal.get_or_insert_default().font_size = value;
6960 },
6961 }),
6962 metadata: None,
6963 files: USER,
6964 }),
6965 SettingsPageItem::SettingItem(SettingItem {
6966 title: "Font Family",
6967 description: "Font family for terminal text. If not set, defaults to buffer font family.",
6968 field: Box::new(SettingField {
6969 organization_override: None,
6970 json_path: Some("terminal.font_family"),
6971 pick: |settings_content| {
6972 settings_content
6973 .terminal
6974 .as_ref()
6975 .and_then(|terminal| terminal.font_family.as_ref())
6976 .or(settings_content.theme.buffer_font_family.as_ref())
6977 },
6978 write: |settings_content, value, _| {
6979 settings_content
6980 .terminal
6981 .get_or_insert_default()
6982 .font_family = value;
6983 },
6984 }),
6985 metadata: None,
6986 files: USER,
6987 }),
6988 SettingsPageItem::SettingItem(SettingItem {
6989 title: "Font Fallbacks",
6990 description: "Font fallbacks for terminal text. If not set, defaults to buffer font fallbacks.",
6991 field: Box::new(
6992 SettingField {
6993 organization_override: None,
6994 json_path: Some("terminal.font_fallbacks"),
6995 pick: |settings_content| {
6996 settings_content
6997 .terminal
6998 .as_ref()
6999 .and_then(|terminal| terminal.font_fallbacks.as_ref())
7000 .or(settings_content.theme.buffer_font_fallbacks.as_ref())
7001 },
7002 write: |settings_content, value, _| {
7003 settings_content
7004 .terminal
7005 .get_or_insert_default()
7006 .font_fallbacks = value;
7007 },
7008 }
7009 .unimplemented(),
7010 ),
7011 metadata: None,
7012 files: USER,
7013 }),
7014 SettingsPageItem::SettingItem(SettingItem {
7015 title: "Font Weight",
7016 description: "Font weight for terminal text in CSS weight units (100-900).",
7017 field: Box::new(SettingField {
7018 organization_override: None,
7019 json_path: Some("terminal.font_weight"),
7020 pick: |settings_content| {
7021 settings_content.terminal.as_ref()?.font_weight.as_ref()
7022 },
7023 write: |settings_content, value, _| {
7024 settings_content
7025 .terminal
7026 .get_or_insert_default()
7027 .font_weight = value;
7028 },
7029 }),
7030 metadata: None,
7031 files: USER,
7032 }),
7033 SettingsPageItem::SettingItem(SettingItem {
7034 title: "Font Features",
7035 description: "Font features for terminal text.",
7036 field: Box::new(
7037 SettingField {
7038 organization_override: None,
7039 json_path: Some("terminal.font_features"),
7040 pick: |settings_content| {
7041 settings_content
7042 .terminal
7043 .as_ref()
7044 .and_then(|terminal| terminal.font_features.as_ref())
7045 .or(settings_content.theme.buffer_font_features.as_ref())
7046 },
7047 write: |settings_content, value, _| {
7048 settings_content
7049 .terminal
7050 .get_or_insert_default()
7051 .font_features = value;
7052 },
7053 }
7054 .unimplemented(),
7055 ),
7056 metadata: None,
7057 files: USER,
7058 }),
7059 ]
7060 }
7061
7062 fn display_settings_section() -> [SettingsPageItem; 6] {
7063 [
7064 SettingsPageItem::SectionHeader("Display Settings"),
7065 SettingsPageItem::SettingItem(SettingItem {
7066 title: "Line Height",
7067 description: "Line height for terminal text.",
7068 field: Box::new(
7069 SettingField {
7070 organization_override: None,
7071 json_path: Some("terminal.line_height"),
7072 pick: |settings_content| {
7073 settings_content.terminal.as_ref()?.line_height.as_ref()
7074 },
7075 write: |settings_content, value, _| {
7076 settings_content
7077 .terminal
7078 .get_or_insert_default()
7079 .line_height = value;
7080 },
7081 }
7082 .unimplemented(),
7083 ),
7084 metadata: None,
7085 files: USER,
7086 }),
7087 SettingsPageItem::SettingItem(SettingItem {
7088 title: "Cursor Shape",
7089 description: "Default cursor shape for the terminal (bar, block, underline, or hollow).",
7090 field: Box::new(SettingField {
7091 organization_override: None,
7092 json_path: Some("terminal.cursor_shape"),
7093 pick: |settings_content| {
7094 settings_content.terminal.as_ref()?.cursor_shape.as_ref()
7095 },
7096 write: |settings_content, value, _| {
7097 settings_content
7098 .terminal
7099 .get_or_insert_default()
7100 .cursor_shape = value;
7101 },
7102 }),
7103 metadata: None,
7104 files: USER,
7105 }),
7106 SettingsPageItem::SettingItem(SettingItem {
7107 title: "Cursor Blinking",
7108 description: "Sets the cursor blinking behavior in the terminal.",
7109 field: Box::new(SettingField {
7110 organization_override: None,
7111 json_path: Some("terminal.blinking"),
7112 pick: |settings_content| settings_content.terminal.as_ref()?.blinking.as_ref(),
7113 write: |settings_content, value, _| {
7114 settings_content.terminal.get_or_insert_default().blinking = value;
7115 },
7116 }),
7117 metadata: None,
7118 files: USER,
7119 }),
7120 SettingsPageItem::SettingItem(SettingItem {
7121 title: "Alternate Scroll",
7122 description: "Whether alternate scroll mode is active by default (converts mouse scroll to arrow keys in apps like Vim).",
7123 field: Box::new(SettingField {
7124 organization_override: None,
7125 json_path: Some("terminal.alternate_scroll"),
7126 pick: |settings_content| {
7127 settings_content
7128 .terminal
7129 .as_ref()?
7130 .alternate_scroll
7131 .as_ref()
7132 },
7133 write: |settings_content, value, _| {
7134 settings_content
7135 .terminal
7136 .get_or_insert_default()
7137 .alternate_scroll = value;
7138 },
7139 }),
7140 metadata: None,
7141 files: USER,
7142 }),
7143 SettingsPageItem::SettingItem(SettingItem {
7144 title: "Minimum Contrast",
7145 description: "The minimum APCA perceptual contrast between foreground and background colors (0-106).",
7146 field: Box::new(SettingField {
7147 organization_override: None,
7148 json_path: Some("terminal.minimum_contrast"),
7149 pick: |settings_content| {
7150 settings_content
7151 .terminal
7152 .as_ref()?
7153 .minimum_contrast
7154 .as_ref()
7155 },
7156 write: |settings_content, value, _| {
7157 settings_content
7158 .terminal
7159 .get_or_insert_default()
7160 .minimum_contrast = value;
7161 },
7162 }),
7163 metadata: None,
7164 files: USER,
7165 }),
7166 ]
7167 }
7168
7169 fn behavior_settings_section() -> [SettingsPageItem; 6] {
7170 [
7171 SettingsPageItem::SectionHeader("Behavior Settings"),
7172 SettingsPageItem::SettingItem(SettingItem {
7173 title: "Option As Meta",
7174 description: "Whether the option key behaves as the meta key.",
7175 field: Box::new(SettingField {
7176 organization_override: None,
7177 json_path: Some("terminal.option_as_meta"),
7178 pick: |settings_content| {
7179 settings_content.terminal.as_ref()?.option_as_meta.as_ref()
7180 },
7181 write: |settings_content, value, _| {
7182 settings_content
7183 .terminal
7184 .get_or_insert_default()
7185 .option_as_meta = value;
7186 },
7187 }),
7188 metadata: None,
7189 files: USER,
7190 }),
7191 SettingsPageItem::SettingItem(SettingItem {
7192 title: "Copy On Select",
7193 description: "Whether selecting text in the terminal automatically copies to the system clipboard.",
7194 field: Box::new(SettingField {
7195 organization_override: None,
7196 json_path: Some("terminal.copy_on_select"),
7197 pick: |settings_content| {
7198 settings_content.terminal.as_ref()?.copy_on_select.as_ref()
7199 },
7200 write: |settings_content, value, _| {
7201 settings_content
7202 .terminal
7203 .get_or_insert_default()
7204 .copy_on_select = value;
7205 },
7206 }),
7207 metadata: None,
7208 files: USER,
7209 }),
7210 SettingsPageItem::SettingItem(SettingItem {
7211 title: "Keep Selection On Copy",
7212 description: "Whether to keep the text selection after copying it to the clipboard.",
7213 field: Box::new(SettingField {
7214 organization_override: None,
7215 json_path: Some("terminal.keep_selection_on_copy"),
7216 pick: |settings_content| {
7217 settings_content
7218 .terminal
7219 .as_ref()?
7220 .keep_selection_on_copy
7221 .as_ref()
7222 },
7223 write: |settings_content, value, _| {
7224 settings_content
7225 .terminal
7226 .get_or_insert_default()
7227 .keep_selection_on_copy = value;
7228 },
7229 }),
7230 metadata: None,
7231 files: USER,
7232 }),
7233 SettingsPageItem::SettingItem(SettingItem {
7234 title: "Open Links In Mouse Mode",
7235 description: "Whether cmd-click (ctrl-click on Linux and Windows) opens hyperlinks even when the terminal application has enabled mouse reporting. When disabled, these clicks are forwarded to the application; links can still be opened with shift-cmd-click.",
7236 field: Box::new(SettingField {
7237 organization_override: None,
7238 json_path: Some("terminal.open_links_in_mouse_mode"),
7239 pick: |settings_content| {
7240 settings_content
7241 .terminal
7242 .as_ref()?
7243 .open_links_in_mouse_mode
7244 .as_ref()
7245 },
7246 write: |settings_content, value, _| {
7247 settings_content
7248 .terminal
7249 .get_or_insert_default()
7250 .open_links_in_mouse_mode = value;
7251 },
7252 }),
7253 metadata: None,
7254 files: USER,
7255 }),
7256 SettingsPageItem::SettingItem(SettingItem {
7257 title: "Audible Bell",
7258 description: "Whether to play a sound when the BEL character (`\\a`, `0x07`) is printed",
7259 field: Box::new(SettingField {
7260 organization_override: None,
7261 json_path: Some("terminal.bell"),
7262 pick: |settings_content| settings_content.terminal.as_ref()?.bell.as_ref(),
7263 write: |settings_content, value, _| {
7264 settings_content.terminal.get_or_insert_default().bell = value;
7265 },
7266 }),
7267 metadata: None,
7268 files: USER,
7269 }),
7270 ]
7271 }
7272
7273 fn layout_settings_section() -> [SettingsPageItem; 3] {
7274 [
7275 SettingsPageItem::SectionHeader("Layout Settings"),
7276 SettingsPageItem::SettingItem(SettingItem {
7277 title: "Default Width",
7278 description: "Default width when the terminal is docked to the left or right (in pixels).",
7279 field: Box::new(SettingField {
7280 organization_override: None,
7281 json_path: Some("terminal.default_width"),
7282 pick: |settings_content| {
7283 settings_content.terminal.as_ref()?.default_width.as_ref()
7284 },
7285 write: |settings_content, value, _| {
7286 settings_content
7287 .terminal
7288 .get_or_insert_default()
7289 .default_width = value;
7290 },
7291 }),
7292 metadata: None,
7293 files: USER,
7294 }),
7295 SettingsPageItem::SettingItem(SettingItem {
7296 title: "Default Height",
7297 description: "Default height when the terminal is docked to the bottom (in pixels).",
7298 field: Box::new(SettingField {
7299 organization_override: None,
7300 json_path: Some("terminal.default_height"),
7301 pick: |settings_content| {
7302 settings_content.terminal.as_ref()?.default_height.as_ref()
7303 },
7304 write: |settings_content, value, _| {
7305 settings_content
7306 .terminal
7307 .get_or_insert_default()
7308 .default_height = value;
7309 },
7310 }),
7311 metadata: None,
7312 files: USER,
7313 }),
7314 ]
7315 }
7316
7317 fn advanced_settings_section() -> [SettingsPageItem; 3] {
7318 [
7319 SettingsPageItem::SectionHeader("Advanced Settings"),
7320 SettingsPageItem::SettingItem(SettingItem {
7321 title: "Max Scroll History Lines",
7322 description: "Maximum number of lines to keep in scrollback history (max: 100,000; 0 disables scrolling).",
7323 field: Box::new(SettingField {
7324 organization_override: None,
7325 json_path: Some("terminal.max_scroll_history_lines"),
7326 pick: |settings_content| {
7327 settings_content
7328 .terminal
7329 .as_ref()?
7330 .max_scroll_history_lines
7331 .as_ref()
7332 },
7333 write: |settings_content, value, _| {
7334 settings_content
7335 .terminal
7336 .get_or_insert_default()
7337 .max_scroll_history_lines = value;
7338 },
7339 }),
7340 metadata: None,
7341 files: USER,
7342 }),
7343 SettingsPageItem::SettingItem(SettingItem {
7344 title: "Scroll Multiplier",
7345 description: "The multiplier for scrolling in the terminal with the mouse wheel",
7346 field: Box::new(SettingField {
7347 organization_override: None,
7348 json_path: Some("terminal.scroll_multiplier"),
7349 pick: |settings_content| {
7350 settings_content
7351 .terminal
7352 .as_ref()?
7353 .scroll_multiplier
7354 .as_ref()
7355 },
7356 write: |settings_content, value, _| {
7357 settings_content
7358 .terminal
7359 .get_or_insert_default()
7360 .scroll_multiplier = value;
7361 },
7362 }),
7363 metadata: None,
7364 files: USER,
7365 }),
7366 ]
7367 }
7368
7369 fn toolbar_section() -> [SettingsPageItem; 2] {
7370 [
7371 SettingsPageItem::SectionHeader("Toolbar"),
7372 SettingsPageItem::SettingItem(SettingItem {
7373 title: "Breadcrumbs",
7374 description: "Display the terminal title in breadcrumbs inside the terminal pane.",
7375 field: Box::new(SettingField {
7376 organization_override: None,
7377 json_path: Some("terminal.toolbar.breadcrumbs"),
7378 pick: |settings_content| {
7379 settings_content
7380 .terminal
7381 .as_ref()?
7382 .toolbar
7383 .as_ref()?
7384 .breadcrumbs
7385 .as_ref()
7386 },
7387 write: |settings_content, value, _| {
7388 settings_content
7389 .terminal
7390 .get_or_insert_default()
7391 .toolbar
7392 .get_or_insert_default()
7393 .breadcrumbs = value;
7394 },
7395 }),
7396 metadata: None,
7397 files: USER,
7398 }),
7399 ]
7400 }
7401
7402 fn scrollbar_section() -> [SettingsPageItem; 2] {
7403 [
7404 SettingsPageItem::SectionHeader("Scrollbar"),
7405 SettingsPageItem::SettingItem(SettingItem {
7406 title: "Show Scrollbar",
7407 description: "When to show the scrollbar in the terminal.",
7408 field: Box::new(SettingField {
7409 organization_override: None,
7410 json_path: Some("terminal.scrollbar.show"),
7411 pick: |settings_content| {
7412 show_scrollbar_or_editor(settings_content, |settings_content| {
7413 settings_content
7414 .terminal
7415 .as_ref()?
7416 .scrollbar
7417 .as_ref()?
7418 .show
7419 .as_ref()
7420 })
7421 },
7422 write: |settings_content, value, _| {
7423 settings_content
7424 .terminal
7425 .get_or_insert_default()
7426 .scrollbar
7427 .get_or_insert_default()
7428 .show = value;
7429 },
7430 }),
7431 metadata: None,
7432 files: USER,
7433 }),
7434 ]
7435 }
7436
7437 SettingsPage {
7438 title: "Terminal",
7439 items: concat_sections![
7440 environment_section(),
7441 font_section(),
7442 display_settings_section(),
7443 behavior_settings_section(),
7444 layout_settings_section(),
7445 advanced_settings_section(),
7446 toolbar_section(),
7447 scrollbar_section(),
7448 ],
7449 }
7450}
7451
7452fn version_control_page() -> SettingsPage {
7453 fn git_integration_section() -> [SettingsPageItem; 2] {
7454 [
7455 SettingsPageItem::SectionHeader("Git Integration"),
7456 SettingsPageItem::DynamicItem(DynamicItem {
7457 discriminant: SettingItem {
7458 files: USER,
7459 title: "Disable Git Integration",
7460 description: "Disable all Git integration features in Omega.",
7461 field: Box::new(SettingField::<bool> {
7462 organization_override: None,
7463 json_path: Some("git.disable_git"),
7464 pick: |settings_content| {
7465 settings_content
7466 .git
7467 .as_ref()?
7468 .enabled
7469 .as_ref()?
7470 .disable_git
7471 .as_ref()
7472 },
7473 write: |settings_content, value, _| {
7474 settings_content
7475 .git
7476 .get_or_insert_default()
7477 .enabled
7478 .get_or_insert_default()
7479 .disable_git = value;
7480 },
7481 }),
7482 metadata: None,
7483 },
7484 pick_discriminant: |settings_content| {
7485 let disabled = settings_content
7486 .git
7487 .as_ref()?
7488 .enabled
7489 .as_ref()?
7490 .disable_git
7491 .unwrap_or(false);
7492 Some(if disabled { 0 } else { 1 })
7493 },
7494 fields: vec![
7495 vec![],
7496 vec![
7497 SettingItem {
7498 files: USER,
7499 title: "Enable Git Status",
7500 description: "Show Git status information in the editor.",
7501 field: Box::new(SettingField::<bool> {
7502 organization_override: None,
7503 json_path: Some("git.enable_status"),
7504 pick: |settings_content| {
7505 settings_content
7506 .git
7507 .as_ref()?
7508 .enabled
7509 .as_ref()?
7510 .enable_status
7511 .as_ref()
7512 },
7513 write: |settings_content, value, _| {
7514 settings_content
7515 .git
7516 .get_or_insert_default()
7517 .enabled
7518 .get_or_insert_default()
7519 .enable_status = value;
7520 },
7521 }),
7522 metadata: None,
7523 },
7524 SettingItem {
7525 files: USER,
7526 title: "Enable Git Diff",
7527 description: "Show Git diff information in the editor.",
7528 field: Box::new(SettingField::<bool> {
7529 organization_override: None,
7530 json_path: Some("git.enable_diff"),
7531 pick: |settings_content| {
7532 settings_content
7533 .git
7534 .as_ref()?
7535 .enabled
7536 .as_ref()?
7537 .enable_diff
7538 .as_ref()
7539 },
7540 write: |settings_content, value, _| {
7541 settings_content
7542 .git
7543 .get_or_insert_default()
7544 .enabled
7545 .get_or_insert_default()
7546 .enable_diff = value;
7547 },
7548 }),
7549 metadata: None,
7550 },
7551 ],
7552 ],
7553 }),
7554 ]
7555 }
7556
7557 fn git_gutter_section() -> [SettingsPageItem; 3] {
7558 [
7559 SettingsPageItem::SectionHeader("Git Gutter"),
7560 SettingsPageItem::SettingItem(SettingItem {
7561 title: "Visibility",
7562 description: "Control whether Git status is shown in the editor's gutter.",
7563 field: Box::new(SettingField {
7564 organization_override: None,
7565 json_path: Some("git.git_gutter"),
7566 pick: |settings_content| settings_content.git.as_ref()?.git_gutter.as_ref(),
7567 write: |settings_content, value, _| {
7568 settings_content.git.get_or_insert_default().git_gutter = value;
7569 },
7570 }),
7571 metadata: None,
7572 files: USER,
7573 }),
7574 // todo(settings_ui): Figure out the right default for this value in default.json
7575 SettingsPageItem::SettingItem(SettingItem {
7576 title: "Debounce",
7577 description: "Debounce threshold in milliseconds after which changes are reflected in the Git gutter.",
7578 field: Box::new(SettingField {
7579 organization_override: None,
7580 json_path: Some("git.gutter_debounce"),
7581 pick: |settings_content| {
7582 settings_content.git.as_ref()?.gutter_debounce.as_ref()
7583 },
7584 write: |settings_content, value, _| {
7585 settings_content.git.get_or_insert_default().gutter_debounce = value;
7586 },
7587 }),
7588 metadata: None,
7589 files: USER,
7590 }),
7591 ]
7592 }
7593
7594 fn inline_git_blame_section() -> [SettingsPageItem; 6] {
7595 [
7596 SettingsPageItem::SectionHeader("Inline Git Blame"),
7597 SettingsPageItem::DynamicItem(DynamicItem {
7598 discriminant: SettingItem {
7599 title: "Enabled",
7600 description: "Whether or not to show Git blame data for the currently focused line.",
7601 field: Box::new(SettingField {
7602 organization_override: None,
7603 json_path: Some("git.inline_blame.enabled"),
7604 pick: |settings_content| {
7605 settings_content
7606 .git
7607 .as_ref()?
7608 .inline_blame
7609 .as_ref()?
7610 .enabled
7611 .as_ref()
7612 },
7613 write: |settings_content, value, _| {
7614 settings_content
7615 .git
7616 .get_or_insert_default()
7617 .inline_blame
7618 .get_or_insert_default()
7619 .enabled = value;
7620 },
7621 }),
7622 metadata: None,
7623 files: USER,
7624 },
7625 pick_discriminant: |settings_content| {
7626 Some(
7627 *settings_content
7628 .git
7629 .as_ref()?
7630 .inline_blame
7631 .as_ref()?
7632 .enabled
7633 .as_ref()? as usize,
7634 )
7635 },
7636 fields: vec![
7637 vec![],
7638 vec![SettingItem {
7639 title: "Location",
7640 description: "Where to render Git blame when it is enabled.",
7641 field: Box::new(SettingField {
7642 organization_override: None,
7643 json_path: Some("git.inline_blame.location"),
7644 pick: |settings_content| {
7645 settings_content
7646 .git
7647 .as_ref()?
7648 .inline_blame
7649 .as_ref()?
7650 .location
7651 .as_ref()
7652 },
7653 write: |settings_content, value, _| {
7654 settings_content
7655 .git
7656 .get_or_insert_default()
7657 .inline_blame
7658 .get_or_insert_default()
7659 .location = value;
7660 },
7661 }),
7662 metadata: None,
7663 files: USER,
7664 }],
7665 ],
7666 }),
7667 SettingsPageItem::SettingItem(SettingItem {
7668 title: "Delay",
7669 description: "The delay after which the inline blame information is shown.",
7670 field: Box::new(SettingField {
7671 organization_override: None,
7672 json_path: Some("git.inline_blame.delay_ms"),
7673 pick: |settings_content| {
7674 settings_content
7675 .git
7676 .as_ref()?
7677 .inline_blame
7678 .as_ref()?
7679 .delay_ms
7680 .as_ref()
7681 },
7682 write: |settings_content, value, _| {
7683 settings_content
7684 .git
7685 .get_or_insert_default()
7686 .inline_blame
7687 .get_or_insert_default()
7688 .delay_ms = value;
7689 },
7690 }),
7691 metadata: None,
7692 files: USER,
7693 }),
7694 SettingsPageItem::SettingItem(SettingItem {
7695 title: "Padding",
7696 description: "Padding between the end of the source line and the start of the inline blame in columns.",
7697 field: Box::new(SettingField {
7698 organization_override: None,
7699 json_path: Some("git.inline_blame.padding"),
7700 pick: |settings_content| {
7701 settings_content
7702 .git
7703 .as_ref()?
7704 .inline_blame
7705 .as_ref()?
7706 .padding
7707 .as_ref()
7708 },
7709 write: |settings_content, value, _| {
7710 settings_content
7711 .git
7712 .get_or_insert_default()
7713 .inline_blame
7714 .get_or_insert_default()
7715 .padding = value;
7716 },
7717 }),
7718 metadata: None,
7719 files: USER,
7720 }),
7721 SettingsPageItem::SettingItem(SettingItem {
7722 title: "Minimum Column",
7723 description: "The minimum column number at which to show the inline blame information.",
7724 field: Box::new(SettingField {
7725 organization_override: None,
7726 json_path: Some("git.inline_blame.min_column"),
7727 pick: |settings_content| {
7728 settings_content
7729 .git
7730 .as_ref()?
7731 .inline_blame
7732 .as_ref()?
7733 .min_column
7734 .as_ref()
7735 },
7736 write: |settings_content, value, _| {
7737 settings_content
7738 .git
7739 .get_or_insert_default()
7740 .inline_blame
7741 .get_or_insert_default()
7742 .min_column = value;
7743 },
7744 }),
7745 metadata: None,
7746 files: USER,
7747 }),
7748 SettingsPageItem::SettingItem(SettingItem {
7749 title: "Show Commit Summary",
7750 description: "Show commit summary as part of the inline blame.",
7751 field: Box::new(SettingField {
7752 organization_override: None,
7753 json_path: Some("git.inline_blame.show_commit_summary"),
7754 pick: |settings_content| {
7755 settings_content
7756 .git
7757 .as_ref()?
7758 .inline_blame
7759 .as_ref()?
7760 .show_commit_summary
7761 .as_ref()
7762 },
7763 write: |settings_content, value, _| {
7764 settings_content
7765 .git
7766 .get_or_insert_default()
7767 .inline_blame
7768 .get_or_insert_default()
7769 .show_commit_summary = value;
7770 },
7771 }),
7772 metadata: None,
7773 files: USER,
7774 }),
7775 ]
7776 }
7777
7778 fn git_blame_view_section() -> [SettingsPageItem; 2] {
7779 [
7780 SettingsPageItem::SectionHeader("Git Blame View"),
7781 SettingsPageItem::SettingItem(SettingItem {
7782 title: "Show Avatar",
7783 description: "Show the avatar of the author of the commit.",
7784 field: Box::new(SettingField {
7785 organization_override: None,
7786 json_path: Some("git.blame.show_avatar"),
7787 pick: |settings_content| {
7788 settings_content
7789 .git
7790 .as_ref()?
7791 .blame
7792 .as_ref()?
7793 .show_avatar
7794 .as_ref()
7795 },
7796 write: |settings_content, value, _| {
7797 settings_content
7798 .git
7799 .get_or_insert_default()
7800 .blame
7801 .get_or_insert_default()
7802 .show_avatar = value;
7803 },
7804 }),
7805 metadata: None,
7806 files: USER,
7807 }),
7808 ]
7809 }
7810
7811 fn branch_picker_section() -> [SettingsPageItem; 2] {
7812 [
7813 SettingsPageItem::SectionHeader("Branch Picker"),
7814 SettingsPageItem::SettingItem(SettingItem {
7815 title: "Show Author Name",
7816 description: "Show author name as part of the commit information in branch picker.",
7817 field: Box::new(SettingField {
7818 organization_override: None,
7819 json_path: Some("git.branch_picker.show_author_name"),
7820 pick: |settings_content| {
7821 settings_content
7822 .git
7823 .as_ref()?
7824 .branch_picker
7825 .as_ref()?
7826 .show_author_name
7827 .as_ref()
7828 },
7829 write: |settings_content, value, _| {
7830 settings_content
7831 .git
7832 .get_or_insert_default()
7833 .branch_picker
7834 .get_or_insert_default()
7835 .show_author_name = value;
7836 },
7837 }),
7838 metadata: None,
7839 files: USER,
7840 }),
7841 ]
7842 }
7843
7844 fn git_hunks_section() -> [SettingsPageItem; 4] {
7845 [
7846 SettingsPageItem::SectionHeader("Git Hunks"),
7847 SettingsPageItem::SettingItem(SettingItem {
7848 title: "Hunk Style",
7849 description: "How Git hunks are displayed visually in the editor.",
7850 field: Box::new(SettingField {
7851 organization_override: None,
7852 json_path: Some("git.hunk_style"),
7853 pick: |settings_content| settings_content.git.as_ref()?.hunk_style.as_ref(),
7854 write: |settings_content, value, _| {
7855 settings_content.git.get_or_insert_default().hunk_style = value;
7856 },
7857 }),
7858 metadata: None,
7859 files: USER,
7860 }),
7861 SettingsPageItem::SettingItem(SettingItem {
7862 title: "Path Style",
7863 description: "Should the name or path be displayed first in the git view.",
7864 field: Box::new(SettingField {
7865 organization_override: None,
7866 json_path: Some("git.path_style"),
7867 pick: |settings_content| settings_content.git.as_ref()?.path_style.as_ref(),
7868 write: |settings_content, value, _| {
7869 settings_content.git.get_or_insert_default().path_style = value;
7870 },
7871 }),
7872 metadata: None,
7873 files: USER,
7874 }),
7875 SettingsPageItem::SettingItem(SettingItem {
7876 title: "Show Stage/Restore Buttons",
7877 description: "Whether to show the stage and restore buttons on diff hunks.",
7878 field: Box::new(SettingField {
7879 organization_override: None,
7880 json_path: Some("git.show_stage_restore_buttons"),
7881 pick: |settings_content| {
7882 settings_content
7883 .git
7884 .as_ref()?
7885 .show_stage_restore_buttons
7886 .as_ref()
7887 },
7888 write: |settings_content, value, _| {
7889 settings_content
7890 .git
7891 .get_or_insert_default()
7892 .show_stage_restore_buttons = value;
7893 },
7894 }),
7895 metadata: None,
7896 files: USER,
7897 }),
7898 ]
7899 }
7900
7901 fn file_diff_section() -> [SettingsPageItem; 2] {
7902 [
7903 SettingsPageItem::SectionHeader("File Diff"),
7904 SettingsPageItem::SettingItem(SettingItem {
7905 title: "Show Full File by Default",
7906 description: "Whether newly opened file diffs show the full file instead of changes only.",
7907 field: Box::new(SettingField {
7908 organization_override: None,
7909 json_path: Some("git.file_diff.show_full_file"),
7910 pick: |settings_content| {
7911 settings_content
7912 .git
7913 .as_ref()?
7914 .file_diff
7915 .as_ref()?
7916 .show_full_file
7917 .as_ref()
7918 },
7919 write: |settings_content, value, _| {
7920 settings_content
7921 .git
7922 .get_or_insert_default()
7923 .file_diff
7924 .get_or_insert_default()
7925 .show_full_file = value;
7926 },
7927 }),
7928 metadata: None,
7929 files: USER,
7930 }),
7931 ]
7932 }
7933
7934 SettingsPage {
7935 title: "Version Control",
7936 items: concat_sections![
7937 git_integration_section(),
7938 git_gutter_section(),
7939 inline_git_blame_section(),
7940 git_blame_view_section(),
7941 branch_picker_section(),
7942 file_diff_section(),
7943 git_hunks_section(),
7944 ],
7945 }
7946}
7947
7948fn collaboration_page() -> SettingsPage {
7949 fn calls_section() -> [SettingsPageItem; 3] {
7950 [
7951 SettingsPageItem::SectionHeader("Calls"),
7952 SettingsPageItem::SettingItem(SettingItem {
7953 title: "Mute On Join",
7954 description: "Whether the microphone should be muted when joining a channel or a call.",
7955 field: Box::new(SettingField {
7956 organization_override: None,
7957 json_path: Some("calls.mute_on_join"),
7958 pick: |settings_content| settings_content.calls.as_ref()?.mute_on_join.as_ref(),
7959 write: |settings_content, value, _| {
7960 settings_content.calls.get_or_insert_default().mute_on_join = value;
7961 },
7962 }),
7963 metadata: None,
7964 files: USER,
7965 }),
7966 SettingsPageItem::SettingItem(SettingItem {
7967 title: "Share On Join",
7968 description: "Whether your current project should be shared when joining an empty channel.",
7969 field: Box::new(SettingField {
7970 organization_override: None,
7971 json_path: Some("calls.share_on_join"),
7972 pick: |settings_content| {
7973 settings_content.calls.as_ref()?.share_on_join.as_ref()
7974 },
7975 write: |settings_content, value, _| {
7976 settings_content.calls.get_or_insert_default().share_on_join = value;
7977 },
7978 }),
7979 metadata: None,
7980 files: USER,
7981 }),
7982 ]
7983 }
7984
7985 fn audio_settings() -> [SettingsPageItem; 3] {
7986 [
7987 SettingsPageItem::ActionLink(ActionLink {
7988 title: "Test Audio".into(),
7989 description: Some("Test your microphone and speaker setup".into()),
7990 button_text: "Test Audio".into(),
7991 on_click: Arc::new(|_settings_window, window, cx| {
7992 open_audio_test_window(window, cx);
7993 }),
7994 files: USER,
7995 }),
7996 SettingsPageItem::SettingItem(SettingItem {
7997 title: "Output Audio Device",
7998 description: "Select output audio device",
7999 field: Box::new(SettingField {
8000 organization_override: None,
8001 json_path: Some("audio.experimental.output_audio_device"),
8002 pick: |settings_content| {
8003 settings_content
8004 .audio
8005 .as_ref()?
8006 .output_audio_device
8007 .as_ref()
8008 .or(DEFAULT_EMPTY_AUDIO_OUTPUT)
8009 },
8010 write: |settings_content, value, _| {
8011 settings_content
8012 .audio
8013 .get_or_insert_default()
8014 .output_audio_device = value;
8015 },
8016 }),
8017 metadata: None,
8018 files: USER,
8019 }),
8020 SettingsPageItem::SettingItem(SettingItem {
8021 title: "Input Audio Device",
8022 description: "Select input audio device",
8023 field: Box::new(SettingField {
8024 organization_override: None,
8025 json_path: Some("audio.experimental.input_audio_device"),
8026 pick: |settings_content| {
8027 settings_content
8028 .audio
8029 .as_ref()?
8030 .input_audio_device
8031 .as_ref()
8032 .or(DEFAULT_EMPTY_AUDIO_INPUT)
8033 },
8034 write: |settings_content, value, _| {
8035 settings_content
8036 .audio
8037 .get_or_insert_default()
8038 .input_audio_device = value;
8039 },
8040 }),
8041 metadata: None,
8042 files: USER,
8043 }),
8044 ]
8045 }
8046
8047 SettingsPage {
8048 title: "Collaboration",
8049 items: concat_sections![calls_section(), audio_settings()],
8050 }
8051}
8052
8053fn ai_page(cx: &App) -> SettingsPage {
8054 fn general_section() -> [SettingsPageItem; 6] {
8055 [
8056 SettingsPageItem::SectionHeader("General"),
8057 SettingsPageItem::SettingItem(SettingItem {
8058 title: "Disable AI",
8059 description: "Whether to disable all AI features in Omega.",
8060 field: Box::new(SettingField {
8061 organization_override: None,
8062 json_path: Some("disable_ai"),
8063 pick: |settings_content| settings_content.project.disable_ai.as_ref(),
8064 write: |settings_content, value, _| {
8065 settings_content.project.disable_ai = value;
8066 },
8067 }),
8068 metadata: None,
8069 files: USER | PROJECT,
8070 }),
8071 SettingsPageItem::SettingItem(SettingItem {
8072 title: "Threads Sidebar Side",
8073 description: "Which side of the window the threads sidebar appears on.",
8074 field: Box::new(SettingField {
8075 organization_override: None,
8076 json_path: Some("agent.sidebar_side"),
8077 pick: |settings_content| settings_content.agent.as_ref()?.sidebar_side.as_ref(),
8078 write: |settings_content, value, _| {
8079 settings_content.agent.get_or_insert_default().sidebar_side = value;
8080 },
8081 }),
8082 metadata: None,
8083 files: USER,
8084 }),
8085 SettingsPageItem::SubPageLink(SubPageLink {
8086 title: "LLM Providers".into(),
8087 r#type: Default::default(),
8088 json_path: Some("llm_providers"),
8089 description: Some("Configure natively-included model providers.".into()),
8090 search_aliases: &[
8091 "ai",
8092 "amazon",
8093 "anthropic",
8094 "api key",
8095 "azure",
8096 "bedrock",
8097 "chat",
8098 "claude",
8099 "copilot",
8100 "gemini",
8101 "github",
8102 "google",
8103 "gpt",
8104 "grok",
8105 "llama",
8106 "llm",
8107 "lm studio",
8108 "mistral",
8109 "ollama",
8110 "openai",
8111 "opencode",
8112 "provider",
8113 "vercel",
8114 "xai",
8115 ],
8116 in_json: false,
8117 files: USER,
8118 render: render_llm_providers_page,
8119 }),
8120 SettingsPageItem::SubPageLink(SubPageLink {
8121 title: "External Agents".into(),
8122 r#type: Default::default(),
8123 json_path: Some("agent_servers"),
8124 description: Some(
8125 "View, add, and remove agents connected through the Agent Client Protocol."
8126 .into(),
8127 ),
8128 search_aliases: &[
8129 "acp",
8130 "agent client protocol",
8131 "amp",
8132 "claude agent",
8133 "claude code",
8134 "codex",
8135 "copilot cli",
8136 "cursor",
8137 "external agent",
8138 "factory droid",
8139 "github copilot",
8140 "grok build",
8141 "junie",
8142 "opencode",
8143 ],
8144 in_json: false,
8145 files: USER,
8146 render: render_external_agents_page,
8147 }),
8148 SettingsPageItem::SubPageLink(SubPageLink {
8149 title: "MCP Servers".into(),
8150 r#type: Default::default(),
8151 json_path: Some("context_servers"),
8152 description: Some(
8153 "View, add, configure, and remove Model Context Protocol servers.".into(),
8154 ),
8155 search_aliases: &["context server", "mcp", "model context protocol"],
8156 in_json: false,
8157 files: USER,
8158 render: render_mcp_servers_page,
8159 }),
8160 ]
8161 }
8162
8163 fn agent_configuration_section(_cx: &App) -> Box<[SettingsPageItem]> {
8164 let mut items = vec![SettingsPageItem::SectionHeader("Agent Configuration")];
8165
8166 items.extend([
8167 SettingsPageItem::SubPageLink(SubPageLink {
8168 title: "Skills".into(),
8169 r#type: Default::default(),
8170 json_path: Some(zed_actions::AGENT_SKILLS_SETTINGS_PATH),
8171 description: Some("View and manage agent skills installed globally or in project worktrees.".into()),
8172 search_aliases: &["agent skill", "agent skills", "custom instructions", "skill", "skills"],
8173 in_json: false,
8174 files: USER | PROJECT,
8175 render: render_skills_setup_page,
8176 }),
8177 SettingsPageItem::SubPageLink(SubPageLink {
8178 title: "Sandbox".into(),
8179 r#type: Default::default(),
8180 json_path: Some(zed_actions::AGENT_SANDBOX_SETTINGS_PATH),
8181 description: Some(
8182 "Review and change the elevated terminal sandbox permissions that are always allowed without prompting."
8183 .into(),
8184 ),
8185 search_aliases: &[
8186 "allow",
8187 "domain",
8188 "filesystem",
8189 "network",
8190 "sandbox",
8191 "unsandboxed",
8192 "permissions",
8193 ],
8194 in_json: true,
8195 files: USER,
8196 render: render_sandbox_settings_page,
8197 }),
8198 SettingsPageItem::SubPageLink(SubPageLink {
8199 title: "Tool Permissions".into(),
8200 r#type: Default::default(),
8201 json_path: Some("agent.tool_permissions"),
8202 description: Some("Set up regex patterns to auto-allow, auto-deny, or always request confirmation, for specific tool inputs.".into()),
8203 search_aliases: &[],
8204 in_json: true,
8205 files: USER,
8206 render: render_tool_permissions_setup_page,
8207 }),
8208 ]);
8209
8210 items.extend([
8211 SettingsPageItem::SettingItem(SettingItem {
8212 title: "Single File Review",
8213 description: "When enabled, agent edits will also be displayed in single-file buffers for review.",
8214 field: Box::new(SettingField {
8215 organization_override: None,
8216 json_path: Some("agent.single_file_review"),
8217 pick: |settings_content| {
8218 settings_content.agent.as_ref()?.single_file_review.as_ref()
8219 },
8220 write: |settings_content, value, _| {
8221 settings_content
8222 .agent
8223 .get_or_insert_default()
8224 .single_file_review = value;
8225 },
8226 }),
8227 metadata: None,
8228 files: USER,
8229 }),
8230 SettingsPageItem::SettingItem(SettingItem {
8231 title: "Enable Feedback",
8232 description: "Show voting thumbs up/down icon buttons for feedback on agent edits.",
8233 field: Box::new(SettingField {
8234 organization_override: Some(|org_config| if org_config.is_agent_thread_feedback_enabled {
8235 None
8236 } else {
8237 Some(&false)
8238 }),
8239 json_path: Some("agent.enable_feedback"),
8240 pick: |settings_content| {
8241 settings_content.agent.as_ref()?.enable_feedback.as_ref()
8242 },
8243 write: |settings_content, value, _| {
8244 settings_content
8245 .agent
8246 .get_or_insert_default()
8247 .enable_feedback = value;
8248 },
8249 }),
8250 metadata: None,
8251 files: USER,
8252 }),
8253 SettingsPageItem::SettingItem(SettingItem {
8254 title: "Notify When Agent Waiting",
8255 description: "Where to show notifications when the agent has completed its response or needs confirmation before running a tool action.",
8256 field: Box::new(SettingField {
8257 organization_override: None,
8258 json_path: Some("agent.notify_when_agent_waiting"),
8259 pick: |settings_content| {
8260 settings_content
8261 .agent
8262 .as_ref()?
8263 .notify_when_agent_waiting
8264 .as_ref()
8265 },
8266 write: |settings_content, value, _| {
8267 settings_content
8268 .agent
8269 .get_or_insert_default()
8270 .notify_when_agent_waiting = value;
8271 },
8272 }),
8273 metadata: None,
8274 files: USER,
8275 }),
8276 SettingsPageItem::SettingItem(SettingItem {
8277 title: "Play Sound When Agent Done",
8278 description: "When to play a sound when the agent has either completed its response, or needs user input.",
8279 field: Box::new(SettingField {
8280 organization_override: None,
8281 json_path: Some("agent.play_sound_when_agent_done"),
8282 pick: |settings_content| {
8283 settings_content
8284 .agent
8285 .as_ref()?
8286 .play_sound_when_agent_done
8287 .as_ref()
8288 },
8289 write: |settings_content, value, _| {
8290 settings_content
8291 .agent
8292 .get_or_insert_default()
8293 .play_sound_when_agent_done = value;
8294 },
8295 }),
8296 metadata: None,
8297 files: USER,
8298 }),
8299 SettingsPageItem::SettingItem(SettingItem {
8300 title: "Expand Edit Card",
8301 description: "Whether to have edit cards in the agent panel expanded, showing a Preview of the diff.",
8302 field: Box::new(SettingField {
8303 organization_override: None,
8304 json_path: Some("agent.expand_edit_card"),
8305 pick: |settings_content| {
8306 settings_content.agent.as_ref()?.expand_edit_card.as_ref()
8307 },
8308 write: |settings_content, value, _| {
8309 settings_content
8310 .agent
8311 .get_or_insert_default()
8312 .expand_edit_card = value;
8313 },
8314 }),
8315 metadata: None,
8316 files: USER,
8317 }),
8318 SettingsPageItem::SettingItem(SettingItem {
8319 title: "Expand Terminal Card",
8320 description: "Whether to have terminal cards in the agent panel expanded, showing the whole command output.",
8321 field: Box::new(SettingField {
8322 organization_override: None,
8323 json_path: Some("agent.expand_terminal_card"),
8324 pick: |settings_content| {
8325 settings_content
8326 .agent
8327 .as_ref()?
8328 .expand_terminal_card
8329 .as_ref()
8330 },
8331 write: |settings_content, value, _| {
8332 settings_content
8333 .agent
8334 .get_or_insert_default()
8335 .expand_terminal_card = value;
8336 },
8337 }),
8338 metadata: None,
8339 files: USER,
8340 }),
8341 SettingsPageItem::SettingItem(SettingItem {
8342 title: "Terminal Thread Init Command",
8343 description: "Command to automatically run when Omega creates a Terminal Thread shell in the agent panel. Runs in your configured shell.",
8344 field: Box::new(SettingField {
8345 organization_override: None,
8346 json_path: Some("agent.terminal_init_command"),
8347 pick: |settings_content| {
8348 settings_content
8349 .agent
8350 .as_ref()?
8351 .terminal_init_command
8352 .as_ref()
8353 },
8354 write: |settings_content, value, _| {
8355 settings_content
8356 .agent
8357 .get_or_insert_default()
8358 .terminal_init_command = value;
8359 },
8360 }),
8361 metadata: Some(Box::new(SettingsFieldMetadata {
8362 placeholder: Some("e.g. claude"),
8363 display_confirm_button: true,
8364 display_clear_button: true,
8365 confirm_on_focus_out: true,
8366 treat_missing_text_as_empty: true,
8367 ..Default::default()
8368 })),
8369 files: USER,
8370 }),
8371 SettingsPageItem::SettingItem(SettingItem {
8372 title: "Thinking Display",
8373 description: "How thinking blocks should be displayed by default. 'Auto' fully expands during streaming, then auto-collapses when done. 'Preview' auto-expands with a height constraint during streaming. 'Always Expanded' shows full content. 'Always Collapsed' keeps them collapsed.",
8374 field: Box::new(SettingField {
8375 organization_override: None,
8376 json_path: Some("agent.thinking_display"),
8377 pick: |settings_content| {
8378 settings_content
8379 .agent
8380 .as_ref()?
8381 .thinking_display
8382 .as_ref()
8383 },
8384 write: |settings_content, value, _| {
8385 settings_content
8386 .agent
8387 .get_or_insert_default()
8388 .thinking_display = value;
8389 },
8390 }),
8391 metadata: None,
8392 files: USER,
8393 }),
8394 SettingsPageItem::SettingItem(SettingItem {
8395 title: "Cancel Generation On Terminal Stop",
8396 description: "Whether clicking the stop button on a running terminal tool should also cancel the agent's generation. Note that this only applies to the stop button, not to ctrl+c inside the terminal.",
8397 field: Box::new(SettingField {
8398 organization_override: None,
8399 json_path: Some("agent.cancel_generation_on_terminal_stop"),
8400 pick: |settings_content| {
8401 settings_content
8402 .agent
8403 .as_ref()?
8404 .cancel_generation_on_terminal_stop
8405 .as_ref()
8406 },
8407 write: |settings_content, value, _| {
8408 settings_content
8409 .agent
8410 .get_or_insert_default()
8411 .cancel_generation_on_terminal_stop = value;
8412 },
8413 }),
8414 metadata: None,
8415 files: USER,
8416 }),
8417 SettingsPageItem::SettingItem(SettingItem {
8418 title: "Use Modifier To Send",
8419 description: "Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages.",
8420 field: Box::new(SettingField {
8421 organization_override: None,
8422 json_path: Some("agent.use_modifier_to_send"),
8423 pick: |settings_content| {
8424 settings_content
8425 .agent
8426 .as_ref()?
8427 .use_modifier_to_send
8428 .as_ref()
8429 },
8430 write: |settings_content, value, _| {
8431 settings_content
8432 .agent
8433 .get_or_insert_default()
8434 .use_modifier_to_send = value;
8435 },
8436 }),
8437 metadata: None,
8438 files: USER,
8439 }),
8440 SettingsPageItem::SettingItem(SettingItem {
8441 title: "Message Editor Min Lines",
8442 description: "Minimum number of lines to display in the agent message editor.",
8443 field: Box::new(SettingField {
8444 organization_override: None,
8445 json_path: Some("agent.message_editor_min_lines"),
8446 pick: |settings_content| {
8447 settings_content
8448 .agent
8449 .as_ref()?
8450 .message_editor_min_lines
8451 .as_ref()
8452 },
8453 write: |settings_content, value, _| {
8454 settings_content
8455 .agent
8456 .get_or_insert_default()
8457 .message_editor_min_lines = value;
8458 },
8459 }),
8460 metadata: None,
8461 files: USER,
8462 }),
8463 SettingsPageItem::SettingItem(SettingItem {
8464 title: "Show Turn Stats",
8465 description: "Whether to show turn statistics like elapsed time during generation and final turn duration.",
8466 field: Box::new(SettingField {
8467 organization_override: None,
8468 json_path: Some("agent.show_turn_stats"),
8469 pick: |settings_content| {
8470 settings_content.agent.as_ref()?.show_turn_stats.as_ref()
8471 },
8472 write: |settings_content, value, _| {
8473 settings_content
8474 .agent
8475 .get_or_insert_default()
8476 .show_turn_stats = value;
8477 },
8478 }),
8479 metadata: None,
8480 files: USER,
8481 }),
8482 SettingsPageItem::SettingItem(SettingItem {
8483 title: "Show Merge Conflict Indicator",
8484 description: "Whether to show the merge conflict indicator in the status bar that offers to resolve conflicts using the agent.",
8485 field: Box::new(SettingField {
8486 organization_override: None,
8487 json_path: Some("agent.show_merge_conflict_indicator"),
8488 pick: |settings_content| {
8489 settings_content.agent.as_ref()?.show_merge_conflict_indicator.as_ref()
8490 },
8491 write: |settings_content, value, _| {
8492 settings_content
8493 .agent
8494 .get_or_insert_default()
8495 .show_merge_conflict_indicator = value;
8496 },
8497 }),
8498 metadata: None,
8499 files: USER,
8500 }),
8501 ]);
8502
8503 items.extend([
8504 SettingsPageItem::SettingItem(SettingItem {
8505 title: "Auto Compact",
8506 description: "Automatically compact the agent's context when it grows too large, summarizing earlier messages to free up room in the model's context window.",
8507 field: Box::new(SettingField {
8508 organization_override: None,
8509 json_path: Some("agent.auto_compact.enabled"),
8510 pick: |settings_content| {
8511 settings_content
8512 .agent
8513 .as_ref()?
8514 .auto_compact
8515 .as_ref()?
8516 .enabled
8517 .as_ref()
8518 },
8519 write: |settings_content, value, _| {
8520 settings_content
8521 .agent
8522 .get_or_insert_default()
8523 .auto_compact
8524 .get_or_insert_default()
8525 .enabled = value;
8526 },
8527 }),
8528 metadata: None,
8529 files: USER,
8530 }),
8531 SettingsPageItem::SettingItem(SettingItem {
8532 title: "Auto Compact Threshold",
8533 description: "When auto compaction runs. A percentage string like \"90%\" is measured against the context window. A positive integer is the number of used tokens to compact after. A negative integer is the number of tokens remaining in the context window before compacting.",
8534 field: Box::new(SettingField {
8535 organization_override: None,
8536 json_path: Some("agent.auto_compact.threshold"),
8537 pick: |settings_content| {
8538 settings_content
8539 .agent
8540 .as_ref()?
8541 .auto_compact
8542 .as_ref()?
8543 .threshold
8544 .as_ref()
8545 },
8546 write: |settings_content, value, _| {
8547 settings_content
8548 .agent
8549 .get_or_insert_default()
8550 .auto_compact
8551 .get_or_insert_default()
8552 .threshold = value;
8553 },
8554 }),
8555 metadata: Some(Box::new(SettingsFieldMetadata {
8556 placeholder: Some("90%"),
8557 ..Default::default()
8558 })),
8559 files: USER,
8560 }),
8561 ]);
8562
8563 items.into_boxed_slice()
8564 }
8565
8566 fn edit_prediction_display_sub_section() -> [SettingsPageItem; 1] {
8567 [SettingsPageItem::SettingItem(SettingItem {
8568 title: "Display Mode",
8569 description: "When to show edit predictions previews in buffer. The eager mode displays them inline, while the subtle mode displays them only when holding a modifier key.",
8570 field: Box::new(SettingField {
8571 organization_override: None,
8572 json_path: Some("edit_prediction.display_mode"),
8573 pick: |settings_content| {
8574 settings_content
8575 .project
8576 .all_languages
8577 .edit_predictions
8578 .as_ref()?
8579 .mode
8580 .as_ref()
8581 },
8582 write: |settings_content, value, _| {
8583 settings_content
8584 .project
8585 .all_languages
8586 .edit_predictions
8587 .get_or_insert_default()
8588 .mode = value;
8589 },
8590 }),
8591 metadata: None,
8592 files: USER,
8593 })]
8594 }
8595
8596 SettingsPage {
8597 title: "AI",
8598 items: concat_sections!(
8599 @vec,
8600 general_section(),
8601 agent_configuration_section(cx),
8602 edit_prediction_language_settings_section(),
8603 edit_prediction_display_sub_section(),
8604 )
8605 .into(),
8606 }
8607}
8608
8609fn network_page() -> SettingsPage {
8610 fn network_section() -> [SettingsPageItem; 3] {
8611 [
8612 SettingsPageItem::SectionHeader("Network"),
8613 SettingsPageItem::SettingItem(SettingItem {
8614 title: "Proxy",
8615 description: "The proxy to use for network requests.",
8616 field: Box::new(SettingField {
8617 organization_override: None,
8618 json_path: Some("proxy"),
8619 pick: |settings_content| settings_content.proxy.as_ref(),
8620 write: |settings_content, value, _| {
8621 settings_content.proxy = value;
8622 },
8623 }),
8624 metadata: Some(Box::new(SettingsFieldMetadata {
8625 placeholder: Some("socks5h://localhost:10808"),
8626 ..Default::default()
8627 })),
8628 files: USER,
8629 }),
8630 SettingsPageItem::SettingItem(SettingItem {
8631 title: "Server URL",
8632 description: "The URL of the collaboration server to connect to.",
8633 field: Box::new(SettingField {
8634 organization_override: None,
8635 json_path: Some("server_url"),
8636 pick: |settings_content| settings_content.server_url.as_ref(),
8637 write: |settings_content, value, _| {
8638 settings_content.server_url = value;
8639 },
8640 }),
8641 metadata: Some(Box::new(SettingsFieldMetadata {
8642 placeholder: Some("https://services.openagents.invalid"),
8643 ..Default::default()
8644 })),
8645 files: USER,
8646 }),
8647 ]
8648 }
8649
8650 SettingsPage {
8651 title: "Network",
8652 items: concat_sections![network_section()],
8653 }
8654}
8655
8656fn language_settings_field<T>(
8657 settings_content: &SettingsContent,
8658 get_language_setting_field: fn(&LanguageSettingsContent) -> Option<&T>,
8659) -> Option<&T> {
8660 let all_languages = &settings_content.project.all_languages;
8661
8662 active_language()
8663 .and_then(|current_language_name| {
8664 all_languages
8665 .languages
8666 .0
8667 .get(current_language_name.as_ref())
8668 })
8669 .and_then(get_language_setting_field)
8670 .or_else(|| get_language_setting_field(&all_languages.defaults))
8671}
8672
8673fn language_settings_field_mut<T>(
8674 settings_content: &mut SettingsContent,
8675 value: Option<T>,
8676 write: fn(&mut LanguageSettingsContent, Option<T>),
8677) {
8678 let all_languages = &mut settings_content.project.all_languages;
8679 let language_content = if let Some(current_language) = active_language() {
8680 all_languages
8681 .languages
8682 .0
8683 .entry(current_language.to_string())
8684 .or_default()
8685 } else {
8686 &mut all_languages.defaults
8687 };
8688 write(language_content, value);
8689}
8690
8691fn language_settings_data() -> Box<[SettingsPageItem]> {
8692 fn indentation_section() -> [SettingsPageItem; 5] {
8693 [
8694 SettingsPageItem::SectionHeader("Indentation"),
8695 SettingsPageItem::SettingItem(SettingItem {
8696 title: "Tab Size",
8697 description: "How many columns a tab should occupy.",
8698 field: Box::new(SettingField {
8699 organization_override: None,
8700 json_path: Some("languages.$(language).tab_size"), // TODO(cameron): not JQ syntax because not URL-safe
8701 pick: |settings_content| {
8702 language_settings_field(settings_content, |language| {
8703 language.tab_size.as_ref()
8704 })
8705 },
8706 write: |settings_content, value, _| {
8707 language_settings_field_mut(settings_content, value, |language, value| {
8708 language.tab_size = value;
8709 })
8710 },
8711 }),
8712 metadata: None,
8713 files: USER | PROJECT,
8714 }),
8715 SettingsPageItem::SettingItem(SettingItem {
8716 title: "Hard Tabs",
8717 description: "Whether to indent lines using tab characters, as opposed to multiple spaces.",
8718 field: Box::new(SettingField {
8719 organization_override: None,
8720 json_path: Some("languages.$(language).hard_tabs"),
8721 pick: |settings_content| {
8722 language_settings_field(settings_content, |language| {
8723 language.hard_tabs.as_ref()
8724 })
8725 },
8726 write: |settings_content, value, _| {
8727 language_settings_field_mut(settings_content, value, |language, value| {
8728 language.hard_tabs = value;
8729 })
8730 },
8731 }),
8732 metadata: None,
8733 files: USER | PROJECT,
8734 }),
8735 SettingsPageItem::SettingItem(SettingItem {
8736 title: "Auto Indent",
8737 description: "Controls automatic indentation behavior when typing.",
8738 field: Box::new(SettingField {
8739 organization_override: None,
8740 json_path: Some("languages.$(language).auto_indent"),
8741 pick: |settings_content| {
8742 language_settings_field(settings_content, |language| {
8743 language.auto_indent.as_ref()
8744 })
8745 },
8746 write: |settings_content, value, _| {
8747 language_settings_field_mut(settings_content, value, |language, value| {
8748 language.auto_indent = value;
8749 })
8750 },
8751 }),
8752 metadata: None,
8753 files: USER | PROJECT,
8754 }),
8755 SettingsPageItem::SettingItem(SettingItem {
8756 title: "Auto Indent On Paste",
8757 description: "Whether indentation of pasted content should be adjusted based on the context.",
8758 field: Box::new(SettingField {
8759 organization_override: None,
8760 json_path: Some("languages.$(language).auto_indent_on_paste"),
8761 pick: |settings_content| {
8762 language_settings_field(settings_content, |language| {
8763 language.auto_indent_on_paste.as_ref()
8764 })
8765 },
8766 write: |settings_content, value, _| {
8767 language_settings_field_mut(settings_content, value, |language, value| {
8768 language.auto_indent_on_paste = value;
8769 })
8770 },
8771 }),
8772 metadata: None,
8773 files: USER | PROJECT,
8774 }),
8775 ]
8776 }
8777
8778 fn wrapping_section() -> [SettingsPageItem; 6] {
8779 [
8780 SettingsPageItem::SectionHeader("Wrapping"),
8781 SettingsPageItem::SettingItem(SettingItem {
8782 title: "Soft Wrap",
8783 description: "How to soft-wrap long lines of text.",
8784 field: Box::new(SettingField {
8785 organization_override: None,
8786 json_path: Some("languages.$(language).soft_wrap"),
8787 pick: |settings_content| {
8788 language_settings_field(settings_content, |language| {
8789 language.soft_wrap.as_ref()
8790 })
8791 },
8792 write: |settings_content, value, _| {
8793 language_settings_field_mut(settings_content, value, |language, value| {
8794 language.soft_wrap = value;
8795 })
8796 },
8797 }),
8798 metadata: None,
8799 files: USER | PROJECT,
8800 }),
8801 SettingsPageItem::SettingItem(SettingItem {
8802 title: "Show Wrap Guides",
8803 description: "Show wrap guides in the editor.",
8804 field: Box::new(SettingField {
8805 organization_override: None,
8806 json_path: Some("languages.$(language).show_wrap_guides"),
8807 pick: |settings_content| {
8808 language_settings_field(settings_content, |language| {
8809 language.show_wrap_guides.as_ref()
8810 })
8811 },
8812 write: |settings_content, value, _| {
8813 language_settings_field_mut(settings_content, value, |language, value| {
8814 language.show_wrap_guides = value;
8815 })
8816 },
8817 }),
8818 metadata: None,
8819 files: USER | PROJECT,
8820 }),
8821 SettingsPageItem::SettingItem(SettingItem {
8822 title: "Preferred Line Length",
8823 description: "The column at which to soft-wrap lines, for buffers where soft-wrap is enabled.",
8824 field: Box::new(SettingField {
8825 organization_override: None,
8826 json_path: Some("languages.$(language).preferred_line_length"),
8827 pick: |settings_content| {
8828 language_settings_field(settings_content, |language| {
8829 language.preferred_line_length.as_ref()
8830 })
8831 },
8832 write: |settings_content, value, _| {
8833 language_settings_field_mut(settings_content, value, |language, value| {
8834 language.preferred_line_length = value;
8835 })
8836 },
8837 }),
8838 metadata: None,
8839 files: USER | PROJECT,
8840 }),
8841 SettingsPageItem::SettingItem(SettingItem {
8842 title: "Wrap Guides",
8843 description: "Character counts at which to show wrap guides in the editor.",
8844 field: Box::new(
8845 SettingField {
8846 organization_override: None,
8847 json_path: Some("languages.$(language).wrap_guides"),
8848 pick: |settings_content| {
8849 language_settings_field(settings_content, |language| {
8850 language.wrap_guides.as_ref()
8851 })
8852 },
8853 write: |settings_content, value, _| {
8854 language_settings_field_mut(
8855 settings_content,
8856 value,
8857 |language, value| {
8858 language.wrap_guides = value;
8859 },
8860 )
8861 },
8862 }
8863 .unimplemented(),
8864 ),
8865 metadata: None,
8866 files: USER | PROJECT,
8867 }),
8868 SettingsPageItem::SettingItem(SettingItem {
8869 title: "Allow Rewrap",
8870 description: "Controls where the `editor::rewrap` action is allowed for this language.",
8871 field: Box::new(SettingField {
8872 organization_override: None,
8873 json_path: Some("languages.$(language).allow_rewrap"),
8874 pick: |settings_content| {
8875 language_settings_field(settings_content, |language| {
8876 language.allow_rewrap.as_ref()
8877 })
8878 },
8879 write: |settings_content, value, _| {
8880 language_settings_field_mut(settings_content, value, |language, value| {
8881 language.allow_rewrap = value;
8882 })
8883 },
8884 }),
8885 metadata: None,
8886 files: USER | PROJECT,
8887 }),
8888 ]
8889 }
8890
8891 fn indent_guides_section() -> [SettingsPageItem; 6] {
8892 [
8893 SettingsPageItem::SectionHeader("Indent Guides"),
8894 SettingsPageItem::SettingItem(SettingItem {
8895 title: "Enabled",
8896 description: "Display indent guides in the editor.",
8897 field: Box::new(SettingField {
8898 organization_override: None,
8899 json_path: Some("languages.$(language).indent_guides.enabled"),
8900 pick: |settings_content| {
8901 language_settings_field(settings_content, |language| {
8902 language
8903 .indent_guides
8904 .as_ref()
8905 .and_then(|indent_guides| indent_guides.enabled.as_ref())
8906 })
8907 },
8908 write: |settings_content, value, _| {
8909 language_settings_field_mut(settings_content, value, |language, value| {
8910 language.indent_guides.get_or_insert_default().enabled = value;
8911 })
8912 },
8913 }),
8914 metadata: None,
8915 files: USER | PROJECT,
8916 }),
8917 SettingsPageItem::SettingItem(SettingItem {
8918 title: "Line Width",
8919 description: "The width of the indent guides in pixels, between 1 and 10.",
8920 field: Box::new(SettingField {
8921 organization_override: None,
8922 json_path: Some("languages.$(language).indent_guides.line_width"),
8923 pick: |settings_content| {
8924 language_settings_field(settings_content, |language| {
8925 language
8926 .indent_guides
8927 .as_ref()
8928 .and_then(|indent_guides| indent_guides.line_width.as_ref())
8929 })
8930 },
8931 write: |settings_content, value, _| {
8932 language_settings_field_mut(settings_content, value, |language, value| {
8933 language.indent_guides.get_or_insert_default().line_width = value;
8934 })
8935 },
8936 }),
8937 metadata: None,
8938 files: USER | PROJECT,
8939 }),
8940 SettingsPageItem::SettingItem(SettingItem {
8941 title: "Active Line Width",
8942 description: "The width of the active indent guide in pixels, between 1 and 10.",
8943 field: Box::new(SettingField {
8944 organization_override: None,
8945 json_path: Some("languages.$(language).indent_guides.active_line_width"),
8946 pick: |settings_content| {
8947 language_settings_field(settings_content, |language| {
8948 language
8949 .indent_guides
8950 .as_ref()
8951 .and_then(|indent_guides| indent_guides.active_line_width.as_ref())
8952 })
8953 },
8954 write: |settings_content, value, _| {
8955 language_settings_field_mut(settings_content, value, |language, value| {
8956 language
8957 .indent_guides
8958 .get_or_insert_default()
8959 .active_line_width = value;
8960 })
8961 },
8962 }),
8963 metadata: None,
8964 files: USER | PROJECT,
8965 }),
8966 SettingsPageItem::SettingItem(SettingItem {
8967 title: "Coloring",
8968 description: "Determines how indent guides are colored.",
8969 field: Box::new(SettingField {
8970 organization_override: None,
8971 json_path: Some("languages.$(language).indent_guides.coloring"),
8972 pick: |settings_content| {
8973 language_settings_field(settings_content, |language| {
8974 language
8975 .indent_guides
8976 .as_ref()
8977 .and_then(|indent_guides| indent_guides.coloring.as_ref())
8978 })
8979 },
8980 write: |settings_content, value, _| {
8981 language_settings_field_mut(settings_content, value, |language, value| {
8982 language.indent_guides.get_or_insert_default().coloring = value;
8983 })
8984 },
8985 }),
8986 metadata: None,
8987 files: USER | PROJECT,
8988 }),
8989 SettingsPageItem::SettingItem(SettingItem {
8990 title: "Background Coloring",
8991 description: "Determines how indent guide backgrounds are colored.",
8992 field: Box::new(SettingField {
8993 organization_override: None,
8994 json_path: Some("languages.$(language).indent_guides.background_coloring"),
8995 pick: |settings_content| {
8996 language_settings_field(settings_content, |language| {
8997 language.indent_guides.as_ref().and_then(|indent_guides| {
8998 indent_guides.background_coloring.as_ref()
8999 })
9000 })
9001 },
9002 write: |settings_content, value, _| {
9003 language_settings_field_mut(settings_content, value, |language, value| {
9004 language
9005 .indent_guides
9006 .get_or_insert_default()
9007 .background_coloring = value;
9008 })
9009 },
9010 }),
9011 metadata: None,
9012 files: USER | PROJECT,
9013 }),
9014 ]
9015 }
9016
9017 fn formatting_section() -> [SettingsPageItem; 8] {
9018 [
9019 SettingsPageItem::SectionHeader("Formatting"),
9020 SettingsPageItem::SettingItem(SettingItem {
9021 title: "Format On Save",
9022 description: "On: format the whole buffer.\nOff: do not format.\nModifications: format only lines with unstaged changes; skips formatting when a git diff or LSP range formatting is unavailable.\nModifications If Available: same, but falls back to formatting the whole buffer.",
9023 field: Box::new(
9024 // TODO(settings_ui): this setting should just be a bool
9025 SettingField {
9026 organization_override: None,
9027 json_path: Some("languages.$(language).format_on_save"),
9028 pick: |settings_content| {
9029 language_settings_field(settings_content, |language| {
9030 language.format_on_save.as_ref()
9031 })
9032 },
9033 write: |settings_content, value, _| {
9034 language_settings_field_mut(
9035 settings_content,
9036 value,
9037 |language, value| {
9038 language.format_on_save = value;
9039 },
9040 )
9041 },
9042 },
9043 ),
9044 metadata: None,
9045 files: USER | PROJECT,
9046 }),
9047 SettingsPageItem::SettingItem(SettingItem {
9048 title: "Remove Trailing Whitespace On Save",
9049 description: "Whether or not to remove any trailing whitespace from lines of a buffer before saving it.",
9050 field: Box::new(SettingField {
9051 organization_override: None,
9052 json_path: Some("languages.$(language).remove_trailing_whitespace_on_save"),
9053 pick: |settings_content| {
9054 language_settings_field(settings_content, |language| {
9055 language.remove_trailing_whitespace_on_save.as_ref()
9056 })
9057 },
9058 write: |settings_content, value, _| {
9059 language_settings_field_mut(settings_content, value, |language, value| {
9060 language.remove_trailing_whitespace_on_save = value;
9061 })
9062 },
9063 }),
9064 metadata: None,
9065 files: USER | PROJECT,
9066 }),
9067 SettingsPageItem::SettingItem(SettingItem {
9068 title: "Ensure Final Newline On Save",
9069 description: "Whether or not to ensure there's a single newline at the end of a buffer when saving it.",
9070 field: Box::new(SettingField {
9071 organization_override: None,
9072 json_path: Some("languages.$(language).ensure_final_newline_on_save"),
9073 pick: |settings_content| {
9074 language_settings_field(settings_content, |language| {
9075 language.ensure_final_newline_on_save.as_ref()
9076 })
9077 },
9078 write: |settings_content, value, _| {
9079 language_settings_field_mut(settings_content, value, |language, value| {
9080 language.ensure_final_newline_on_save = value;
9081 })
9082 },
9083 }),
9084 metadata: None,
9085 files: USER | PROJECT,
9086 }),
9087 SettingsPageItem::SettingItem(SettingItem {
9088 title: "Line Ending",
9089 description: "How line endings should be handled for new files and during format and save operations.",
9090 field: Box::new(SettingField {
9091 organization_override: None,
9092 json_path: Some("languages.$(language).line_ending"),
9093 pick: |settings_content| {
9094 language_settings_field(settings_content, |language| {
9095 language.line_ending.as_ref()
9096 })
9097 },
9098 write: |settings_content, value, _| {
9099 language_settings_field_mut(settings_content, value, |language, value| {
9100 language.line_ending = value;
9101 })
9102 },
9103 }),
9104 metadata: Some(Box::new(SettingsFieldMetadata {
9105 should_do_titlecase: Some(false),
9106 ..Default::default()
9107 })),
9108 files: USER | PROJECT,
9109 }),
9110 SettingsPageItem::SettingItem(SettingItem {
9111 title: "Formatter",
9112 description: "How to perform a buffer format.",
9113 field: Box::new(
9114 SettingField {
9115 organization_override: None,
9116 json_path: Some("languages.$(language).formatter"),
9117 pick: |settings_content| {
9118 language_settings_field(settings_content, |language| {
9119 language.formatter.as_ref()
9120 })
9121 },
9122 write: |settings_content, value, _| {
9123 language_settings_field_mut(
9124 settings_content,
9125 value,
9126 |language, value| {
9127 language.formatter = value;
9128 },
9129 )
9130 },
9131 }
9132 .unimplemented(),
9133 ),
9134 metadata: None,
9135 files: USER | PROJECT,
9136 }),
9137 SettingsPageItem::SettingItem(SettingItem {
9138 title: "Use On Type Format",
9139 description: "Whether to use additional LSP queries to format (and amend) the code after every \"trigger\" symbol input, defined by LSP server capabilities",
9140 field: Box::new(SettingField {
9141 organization_override: None,
9142 json_path: Some("languages.$(language).use_on_type_format"),
9143 pick: |settings_content| {
9144 language_settings_field(settings_content, |language| {
9145 language.use_on_type_format.as_ref()
9146 })
9147 },
9148 write: |settings_content, value, _| {
9149 language_settings_field_mut(settings_content, value, |language, value| {
9150 language.use_on_type_format = value;
9151 })
9152 },
9153 }),
9154 metadata: None,
9155 files: USER | PROJECT,
9156 }),
9157 SettingsPageItem::SettingItem(SettingItem {
9158 title: "Code Actions On Format",
9159 description: "Additional code actions to run when formatting.",
9160 field: Box::new(
9161 SettingField {
9162 organization_override: None,
9163 json_path: Some("languages.$(language).code_actions_on_format"),
9164 pick: |settings_content| {
9165 language_settings_field(settings_content, |language| {
9166 language.code_actions_on_format.as_ref()
9167 })
9168 },
9169 write: |settings_content, value, _| {
9170 language_settings_field_mut(
9171 settings_content,
9172 value,
9173 |language, value| {
9174 language.code_actions_on_format = value;
9175 },
9176 )
9177 },
9178 }
9179 .unimplemented(),
9180 ),
9181 metadata: None,
9182 files: USER | PROJECT,
9183 }),
9184 ]
9185 }
9186
9187 fn autoclose_section() -> [SettingsPageItem; 5] {
9188 [
9189 SettingsPageItem::SectionHeader("Autoclose"),
9190 SettingsPageItem::SettingItem(SettingItem {
9191 title: "Use Autoclose",
9192 description: "Whether to automatically type closing characters for you. For example, when you type '(', Omega will automatically add a closing ')' at the correct position.",
9193 field: Box::new(SettingField {
9194 organization_override: None,
9195 json_path: Some("languages.$(language).use_autoclose"),
9196 pick: |settings_content| {
9197 language_settings_field(settings_content, |language| {
9198 language.use_autoclose.as_ref()
9199 })
9200 },
9201 write: |settings_content, value, _| {
9202 language_settings_field_mut(settings_content, value, |language, value| {
9203 language.use_autoclose = value;
9204 })
9205 },
9206 }),
9207 metadata: None,
9208 files: USER | PROJECT,
9209 }),
9210 SettingsPageItem::SettingItem(SettingItem {
9211 title: "Use Auto Surround",
9212 description: "Whether to automatically surround text with characters for you. For example, when you select text and type '(', Omega will automatically surround text with ().",
9213 field: Box::new(SettingField {
9214 organization_override: None,
9215 json_path: Some("languages.$(language).use_auto_surround"),
9216 pick: |settings_content| {
9217 language_settings_field(settings_content, |language| {
9218 language.use_auto_surround.as_ref()
9219 })
9220 },
9221 write: |settings_content, value, _| {
9222 language_settings_field_mut(settings_content, value, |language, value| {
9223 language.use_auto_surround = value;
9224 })
9225 },
9226 }),
9227 metadata: None,
9228 files: USER | PROJECT,
9229 }),
9230 SettingsPageItem::SettingItem(SettingItem {
9231 title: "Always Treat Brackets As Autoclosed",
9232 description: "Controls whether the closing characters are always skipped over and auto-removed no matter how they were inserted.",
9233 field: Box::new(SettingField {
9234 organization_override: None,
9235 json_path: Some("languages.$(language).always_treat_brackets_as_autoclosed"),
9236 pick: |settings_content| {
9237 language_settings_field(settings_content, |language| {
9238 language.always_treat_brackets_as_autoclosed.as_ref()
9239 })
9240 },
9241 write: |settings_content, value, _| {
9242 language_settings_field_mut(settings_content, value, |language, value| {
9243 language.always_treat_brackets_as_autoclosed = value;
9244 })
9245 },
9246 }),
9247 metadata: None,
9248 files: USER | PROJECT,
9249 }),
9250 SettingsPageItem::SettingItem(SettingItem {
9251 title: "JSX Tag Auto Close",
9252 description: "Whether to automatically close JSX tags.",
9253 field: Box::new(SettingField {
9254 organization_override: None,
9255 json_path: Some("languages.$(language).jsx_tag_auto_close"),
9256 // TODO(settings_ui): this setting should just be a bool
9257 pick: |settings_content| {
9258 language_settings_field(settings_content, |language| {
9259 language.jsx_tag_auto_close.as_ref()?.enabled.as_ref()
9260 })
9261 },
9262 write: |settings_content, value, _| {
9263 language_settings_field_mut(settings_content, value, |language, value| {
9264 language.jsx_tag_auto_close.get_or_insert_default().enabled = value;
9265 })
9266 },
9267 }),
9268 metadata: None,
9269 files: USER | PROJECT,
9270 }),
9271 ]
9272 }
9273
9274 fn whitespace_section() -> [SettingsPageItem; 4] {
9275 [
9276 SettingsPageItem::SectionHeader("Whitespace"),
9277 SettingsPageItem::SettingItem(SettingItem {
9278 title: "Show Whitespaces",
9279 description: "Whether to show tabs and spaces in the editor.",
9280 field: Box::new(SettingField {
9281 organization_override: None,
9282 json_path: Some("languages.$(language).show_whitespaces"),
9283 pick: |settings_content| {
9284 language_settings_field(settings_content, |language| {
9285 language.show_whitespaces.as_ref()
9286 })
9287 },
9288 write: |settings_content, value, _| {
9289 language_settings_field_mut(settings_content, value, |language, value| {
9290 language.show_whitespaces = value;
9291 })
9292 },
9293 }),
9294 metadata: None,
9295 files: USER | PROJECT,
9296 }),
9297 SettingsPageItem::SettingItem(SettingItem {
9298 title: "Space Whitespace Indicator",
9299 description: "Visible character used to render space characters when show_whitespaces is enabled (default: \"•\")",
9300 field: Box::new(
9301 SettingField {
9302 organization_override: None,
9303 json_path: Some("languages.$(language).whitespace_map.space"),
9304 pick: |settings_content| {
9305 language_settings_field(settings_content, |language| {
9306 language.whitespace_map.as_ref()?.space.as_ref()
9307 })
9308 },
9309 write: |settings_content, value, _| {
9310 language_settings_field_mut(
9311 settings_content,
9312 value,
9313 |language, value| {
9314 language.whitespace_map.get_or_insert_default().space = value;
9315 },
9316 )
9317 },
9318 }
9319 .unimplemented(),
9320 ),
9321 metadata: None,
9322 files: USER | PROJECT,
9323 }),
9324 SettingsPageItem::SettingItem(SettingItem {
9325 title: "Tab Whitespace Indicator",
9326 description: "Visible character used to render tab characters when show_whitespaces is enabled (default: \"→\")",
9327 field: Box::new(
9328 SettingField {
9329 organization_override: None,
9330 json_path: Some("languages.$(language).whitespace_map.tab"),
9331 pick: |settings_content| {
9332 language_settings_field(settings_content, |language| {
9333 language.whitespace_map.as_ref()?.tab.as_ref()
9334 })
9335 },
9336 write: |settings_content, value, _| {
9337 language_settings_field_mut(
9338 settings_content,
9339 value,
9340 |language, value| {
9341 language.whitespace_map.get_or_insert_default().tab = value;
9342 },
9343 )
9344 },
9345 }
9346 .unimplemented(),
9347 ),
9348 metadata: None,
9349 files: USER | PROJECT,
9350 }),
9351 ]
9352 }
9353
9354 fn completions_section() -> [SettingsPageItem; 8] {
9355 [
9356 SettingsPageItem::SectionHeader("Completions"),
9357 SettingsPageItem::SettingItem(SettingItem {
9358 title: "Show Completions On Input",
9359 description: "Whether to pop the completions menu while typing in an editor without explicitly requesting it.",
9360 field: Box::new(SettingField {
9361 organization_override: None,
9362 json_path: Some("languages.$(language).show_completions_on_input"),
9363 pick: |settings_content| {
9364 language_settings_field(settings_content, |language| {
9365 language.show_completions_on_input.as_ref()
9366 })
9367 },
9368 write: |settings_content, value, _| {
9369 language_settings_field_mut(settings_content, value, |language, value| {
9370 language.show_completions_on_input = value;
9371 })
9372 },
9373 }),
9374 metadata: None,
9375 files: USER | PROJECT,
9376 }),
9377 SettingsPageItem::SettingItem(SettingItem {
9378 title: "Show Completion Documentation",
9379 description: "Whether to display inline and alongside documentation for items in the completions menu.",
9380 field: Box::new(SettingField {
9381 organization_override: None,
9382 json_path: Some("languages.$(language).show_completion_documentation"),
9383 pick: |settings_content| {
9384 language_settings_field(settings_content, |language| {
9385 language.show_completion_documentation.as_ref()
9386 })
9387 },
9388 write: |settings_content, value, _| {
9389 language_settings_field_mut(settings_content, value, |language, value| {
9390 language.show_completion_documentation = value;
9391 })
9392 },
9393 }),
9394 metadata: None,
9395 files: USER | PROJECT,
9396 }),
9397 SettingsPageItem::SettingItem(SettingItem {
9398 title: "Words",
9399 description: "Controls how words are completed.",
9400 field: Box::new(SettingField {
9401 organization_override: None,
9402 json_path: Some("languages.$(language).completions.words"),
9403 pick: |settings_content| {
9404 language_settings_field(settings_content, |language| {
9405 language.completions.as_ref()?.words.as_ref()
9406 })
9407 },
9408 write: |settings_content, value, _| {
9409 language_settings_field_mut(settings_content, value, |language, value| {
9410 language.completions.get_or_insert_default().words = value;
9411 })
9412 },
9413 }),
9414 metadata: None,
9415 files: USER | PROJECT,
9416 }),
9417 SettingsPageItem::SettingItem(SettingItem {
9418 title: "Words Min Length",
9419 description: "How many characters has to be in the completions query to automatically show the words-based completions.",
9420 field: Box::new(SettingField {
9421 organization_override: None,
9422 json_path: Some("languages.$(language).completions.words_min_length"),
9423 pick: |settings_content| {
9424 language_settings_field(settings_content, |language| {
9425 language.completions.as_ref()?.words_min_length.as_ref()
9426 })
9427 },
9428 write: |settings_content, value, _| {
9429 language_settings_field_mut(settings_content, value, |language, value| {
9430 language
9431 .completions
9432 .get_or_insert_default()
9433 .words_min_length = value;
9434 })
9435 },
9436 }),
9437 metadata: None,
9438 files: USER | PROJECT,
9439 }),
9440 SettingsPageItem::SettingItem(SettingItem {
9441 title: "Completion Menu Scrollbar",
9442 description: "When to show the scrollbar in the completion menu.",
9443 field: Box::new(SettingField {
9444 organization_override: None,
9445 json_path: Some("editor.completion_menu_scrollbar"),
9446 pick: |settings_content| {
9447 settings_content.editor.completion_menu_scrollbar.as_ref()
9448 },
9449 write: |settings_content, value, _| {
9450 settings_content.editor.completion_menu_scrollbar = value;
9451 },
9452 }),
9453 metadata: None,
9454 files: USER,
9455 }),
9456 SettingsPageItem::SettingItem(SettingItem {
9457 title: "Completion Detail Alignment",
9458 description: "Whether to align detail text in code completions context menus left or right.",
9459 field: Box::new(SettingField {
9460 organization_override: None,
9461 json_path: Some("editor.completion_detail_alignment"),
9462 pick: |settings_content| {
9463 settings_content.editor.completion_detail_alignment.as_ref()
9464 },
9465 write: |settings_content, value, _| {
9466 settings_content.editor.completion_detail_alignment = value;
9467 },
9468 }),
9469 metadata: None,
9470 files: USER,
9471 }),
9472 SettingsPageItem::SettingItem(SettingItem {
9473 title: "Completion Menu Item Kind",
9474 description: "How to display the LSP item kind (function, method, variable, etc.) of each entry in the completions menu.",
9475 field: Box::new(SettingField {
9476 organization_override: None,
9477 json_path: Some("editor.completion_menu_item_kind"),
9478 pick: |settings_content| {
9479 settings_content.editor.completion_menu_item_kind.as_ref()
9480 },
9481 write: |settings_content, value, _| {
9482 settings_content.editor.completion_menu_item_kind = value;
9483 },
9484 }),
9485 metadata: None,
9486 files: USER,
9487 }),
9488 ]
9489 }
9490
9491 fn inlay_hints_section() -> [SettingsPageItem; 10] {
9492 [
9493 SettingsPageItem::SectionHeader("Inlay Hints"),
9494 SettingsPageItem::SettingItem(SettingItem {
9495 title: "Enabled",
9496 description: "Global switch to toggle hints on and off.",
9497 field: Box::new(SettingField {
9498 organization_override: None,
9499 json_path: Some("languages.$(language).inlay_hints.enabled"),
9500 pick: |settings_content| {
9501 language_settings_field(settings_content, |language| {
9502 language.inlay_hints.as_ref()?.enabled.as_ref()
9503 })
9504 },
9505 write: |settings_content, value, _| {
9506 language_settings_field_mut(settings_content, value, |language, value| {
9507 language.inlay_hints.get_or_insert_default().enabled = value;
9508 })
9509 },
9510 }),
9511 metadata: None,
9512 files: USER | PROJECT,
9513 }),
9514 SettingsPageItem::SettingItem(SettingItem {
9515 title: "Show Value Hints",
9516 description: "Global switch to toggle inline values on and off when debugging.",
9517 field: Box::new(SettingField {
9518 organization_override: None,
9519 json_path: Some("languages.$(language).inlay_hints.show_value_hints"),
9520 pick: |settings_content| {
9521 language_settings_field(settings_content, |language| {
9522 language.inlay_hints.as_ref()?.show_value_hints.as_ref()
9523 })
9524 },
9525 write: |settings_content, value, _| {
9526 language_settings_field_mut(settings_content, value, |language, value| {
9527 language
9528 .inlay_hints
9529 .get_or_insert_default()
9530 .show_value_hints = value;
9531 })
9532 },
9533 }),
9534 metadata: None,
9535 files: USER | PROJECT,
9536 }),
9537 SettingsPageItem::SettingItem(SettingItem {
9538 title: "Show Type Hints",
9539 description: "Whether type hints should be shown.",
9540 field: Box::new(SettingField {
9541 organization_override: None,
9542 json_path: Some("languages.$(language).inlay_hints.show_type_hints"),
9543 pick: |settings_content| {
9544 language_settings_field(settings_content, |language| {
9545 language.inlay_hints.as_ref()?.show_type_hints.as_ref()
9546 })
9547 },
9548 write: |settings_content, value, _| {
9549 language_settings_field_mut(settings_content, value, |language, value| {
9550 language.inlay_hints.get_or_insert_default().show_type_hints = value;
9551 })
9552 },
9553 }),
9554 metadata: None,
9555 files: USER | PROJECT,
9556 }),
9557 SettingsPageItem::SettingItem(SettingItem {
9558 title: "Show Parameter Hints",
9559 description: "Whether parameter hints should be shown.",
9560 field: Box::new(SettingField {
9561 organization_override: None,
9562 json_path: Some("languages.$(language).inlay_hints.show_parameter_hints"),
9563 pick: |settings_content| {
9564 language_settings_field(settings_content, |language| {
9565 language.inlay_hints.as_ref()?.show_parameter_hints.as_ref()
9566 })
9567 },
9568 write: |settings_content, value, _| {
9569 language_settings_field_mut(settings_content, value, |language, value| {
9570 language
9571 .inlay_hints
9572 .get_or_insert_default()
9573 .show_parameter_hints = value;
9574 })
9575 },
9576 }),
9577 metadata: None,
9578 files: USER | PROJECT,
9579 }),
9580 SettingsPageItem::SettingItem(SettingItem {
9581 title: "Show Other Hints",
9582 description: "Whether other hints should be shown.",
9583 field: Box::new(SettingField {
9584 organization_override: None,
9585 json_path: Some("languages.$(language).inlay_hints.show_other_hints"),
9586 pick: |settings_content| {
9587 language_settings_field(settings_content, |language| {
9588 language.inlay_hints.as_ref()?.show_other_hints.as_ref()
9589 })
9590 },
9591 write: |settings_content, value, _| {
9592 language_settings_field_mut(settings_content, value, |language, value| {
9593 language
9594 .inlay_hints
9595 .get_or_insert_default()
9596 .show_other_hints = value;
9597 })
9598 },
9599 }),
9600 metadata: None,
9601 files: USER | PROJECT,
9602 }),
9603 SettingsPageItem::SettingItem(SettingItem {
9604 title: "Show Background",
9605 description: "Show a background for inlay hints.",
9606 field: Box::new(SettingField {
9607 organization_override: None,
9608 json_path: Some("languages.$(language).inlay_hints.show_background"),
9609 pick: |settings_content| {
9610 language_settings_field(settings_content, |language| {
9611 language.inlay_hints.as_ref()?.show_background.as_ref()
9612 })
9613 },
9614 write: |settings_content, value, _| {
9615 language_settings_field_mut(settings_content, value, |language, value| {
9616 language.inlay_hints.get_or_insert_default().show_background = value;
9617 })
9618 },
9619 }),
9620 metadata: None,
9621 files: USER | PROJECT,
9622 }),
9623 SettingsPageItem::SettingItem(SettingItem {
9624 title: "Edit Debounce Ms",
9625 description: "Whether or not to debounce inlay hints updates after buffer edits (set to 0 to disable debouncing).",
9626 field: Box::new(SettingField {
9627 organization_override: None,
9628 json_path: Some("languages.$(language).inlay_hints.edit_debounce_ms"),
9629 pick: |settings_content| {
9630 language_settings_field(settings_content, |language| {
9631 language.inlay_hints.as_ref()?.edit_debounce_ms.as_ref()
9632 })
9633 },
9634 write: |settings_content, value, _| {
9635 language_settings_field_mut(settings_content, value, |language, value| {
9636 language
9637 .inlay_hints
9638 .get_or_insert_default()
9639 .edit_debounce_ms = value;
9640 })
9641 },
9642 }),
9643 metadata: None,
9644 files: USER | PROJECT,
9645 }),
9646 SettingsPageItem::SettingItem(SettingItem {
9647 title: "Scroll Debounce Ms",
9648 description: "Whether or not to debounce inlay hints updates after buffer scrolls (set to 0 to disable debouncing).",
9649 field: Box::new(SettingField {
9650 organization_override: None,
9651 json_path: Some("languages.$(language).inlay_hints.scroll_debounce_ms"),
9652 pick: |settings_content| {
9653 language_settings_field(settings_content, |language| {
9654 language.inlay_hints.as_ref()?.scroll_debounce_ms.as_ref()
9655 })
9656 },
9657 write: |settings_content, value, _| {
9658 language_settings_field_mut(settings_content, value, |language, value| {
9659 language
9660 .inlay_hints
9661 .get_or_insert_default()
9662 .scroll_debounce_ms = value;
9663 })
9664 },
9665 }),
9666 metadata: None,
9667 files: USER | PROJECT,
9668 }),
9669 SettingsPageItem::SettingItem(SettingItem {
9670 title: "Toggle On Modifiers Press",
9671 description: "Toggles inlay hints (hides or shows) when the user presses the modifiers specified.",
9672 field: Box::new(
9673 SettingField {
9674 organization_override: None,
9675 json_path: Some(
9676 "languages.$(language).inlay_hints.toggle_on_modifiers_press",
9677 ),
9678 pick: |settings_content| {
9679 language_settings_field(settings_content, |language| {
9680 language
9681 .inlay_hints
9682 .as_ref()?
9683 .toggle_on_modifiers_press
9684 .as_ref()
9685 })
9686 },
9687 write: |settings_content, value, _| {
9688 language_settings_field_mut(
9689 settings_content,
9690 value,
9691 |language, value| {
9692 language
9693 .inlay_hints
9694 .get_or_insert_default()
9695 .toggle_on_modifiers_press = value;
9696 },
9697 )
9698 },
9699 }
9700 .unimplemented(),
9701 ),
9702 metadata: None,
9703 files: USER | PROJECT,
9704 }),
9705 ]
9706 }
9707
9708 fn tasks_section() -> [SettingsPageItem; 4] {
9709 [
9710 SettingsPageItem::SectionHeader("Tasks"),
9711 SettingsPageItem::SettingItem(SettingItem {
9712 title: "Enabled",
9713 description: "Whether tasks are enabled for this language.",
9714 field: Box::new(SettingField {
9715 organization_override: None,
9716 json_path: Some("languages.$(language).tasks.enabled"),
9717 pick: |settings_content| {
9718 language_settings_field(settings_content, |language| {
9719 language.tasks.as_ref()?.enabled.as_ref()
9720 })
9721 },
9722 write: |settings_content, value, _| {
9723 language_settings_field_mut(settings_content, value, |language, value| {
9724 language.tasks.get_or_insert_default().enabled = value;
9725 })
9726 },
9727 }),
9728 metadata: None,
9729 files: USER | PROJECT,
9730 }),
9731 SettingsPageItem::SettingItem(SettingItem {
9732 title: "Variables",
9733 description: "Extra task variables to set for a particular language.",
9734 field: Box::new(
9735 SettingField {
9736 organization_override: None,
9737 json_path: Some("languages.$(language).tasks.variables"),
9738 pick: |settings_content| {
9739 language_settings_field(settings_content, |language| {
9740 language.tasks.as_ref()?.variables.as_ref()
9741 })
9742 },
9743 write: |settings_content, value, _| {
9744 language_settings_field_mut(
9745 settings_content,
9746 value,
9747 |language, value| {
9748 language.tasks.get_or_insert_default().variables = value;
9749 },
9750 )
9751 },
9752 }
9753 .unimplemented(),
9754 ),
9755 metadata: None,
9756 files: USER | PROJECT,
9757 }),
9758 SettingsPageItem::SettingItem(SettingItem {
9759 title: "Prefer LSP",
9760 description: "Use LSP tasks over Omega language extension tasks.",
9761 field: Box::new(SettingField {
9762 organization_override: None,
9763 json_path: Some("languages.$(language).tasks.prefer_lsp"),
9764 pick: |settings_content| {
9765 language_settings_field(settings_content, |language| {
9766 language.tasks.as_ref()?.prefer_lsp.as_ref()
9767 })
9768 },
9769 write: |settings_content, value, _| {
9770 language_settings_field_mut(settings_content, value, |language, value| {
9771 language.tasks.get_or_insert_default().prefer_lsp = value;
9772 })
9773 },
9774 }),
9775 metadata: None,
9776 files: USER | PROJECT,
9777 }),
9778 ]
9779 }
9780
9781 fn miscellaneous_section() -> [SettingsPageItem; 7] {
9782 [
9783 SettingsPageItem::SectionHeader("Miscellaneous"),
9784 SettingsPageItem::SettingItem(SettingItem {
9785 title: "Word Diff Enabled",
9786 description: "Whether to enable word diff highlighting in the editor. When enabled, changed words within modified lines are highlighted to show exactly what changed.",
9787 field: Box::new(SettingField {
9788 organization_override: None,
9789 json_path: Some("languages.$(language).word_diff_enabled"),
9790 pick: |settings_content| {
9791 language_settings_field(settings_content, |language| {
9792 language.word_diff_enabled.as_ref()
9793 })
9794 },
9795 write: |settings_content, value, _| {
9796 language_settings_field_mut(settings_content, value, |language, value| {
9797 language.word_diff_enabled = value;
9798 })
9799 },
9800 }),
9801 metadata: None,
9802 files: USER | PROJECT,
9803 }),
9804 SettingsPageItem::SettingItem(SettingItem {
9805 title: "Debuggers",
9806 description: "Preferred debuggers for this language.",
9807 field: Box::new(
9808 SettingField {
9809 organization_override: None,
9810 json_path: Some("languages.$(language).debuggers"),
9811 pick: |settings_content| {
9812 language_settings_field(settings_content, |language| {
9813 language.debuggers.as_ref()
9814 })
9815 },
9816 write: |settings_content, value, _| {
9817 language_settings_field_mut(
9818 settings_content,
9819 value,
9820 |language, value| {
9821 language.debuggers = value;
9822 },
9823 )
9824 },
9825 }
9826 .unimplemented(),
9827 ),
9828 metadata: None,
9829 files: USER | PROJECT,
9830 }),
9831 SettingsPageItem::SettingItem(SettingItem {
9832 title: "Middle Click Paste",
9833 description: "Enable middle-click paste on Linux.",
9834 field: Box::new(SettingField {
9835 organization_override: None,
9836 json_path: Some("languages.$(language).editor.middle_click_paste"),
9837 pick: |settings_content| settings_content.editor.middle_click_paste.as_ref(),
9838 write: |settings_content, value, _| {
9839 settings_content.editor.middle_click_paste = value;
9840 },
9841 }),
9842 metadata: None,
9843 files: USER,
9844 }),
9845 SettingsPageItem::SettingItem(SettingItem {
9846 title: "Extend Comment On Newline",
9847 description: "Whether to start a new line with a comment when a previous line is a comment as well.",
9848 field: Box::new(SettingField {
9849 organization_override: None,
9850 json_path: Some("languages.$(language).extend_comment_on_newline"),
9851 pick: |settings_content| {
9852 language_settings_field(settings_content, |language| {
9853 language.extend_comment_on_newline.as_ref()
9854 })
9855 },
9856 write: |settings_content, value, _| {
9857 language_settings_field_mut(settings_content, value, |language, value| {
9858 language.extend_comment_on_newline = value;
9859 })
9860 },
9861 }),
9862 metadata: None,
9863 files: USER | PROJECT,
9864 }),
9865 SettingsPageItem::SettingItem(SettingItem {
9866 title: "Colorize Brackets",
9867 description: "Whether to colorize brackets in the editor.",
9868 field: Box::new(SettingField {
9869 organization_override: None,
9870 json_path: Some("languages.$(language).colorize_brackets"),
9871 pick: |settings_content| {
9872 language_settings_field(settings_content, |language| {
9873 language.colorize_brackets.as_ref()
9874 })
9875 },
9876 write: |settings_content, value, _| {
9877 language_settings_field_mut(settings_content, value, |language, value| {
9878 language.colorize_brackets = value;
9879 })
9880 },
9881 }),
9882 metadata: None,
9883 files: USER | PROJECT,
9884 }),
9885 SettingsPageItem::SettingItem(SettingItem {
9886 title: "Vim/Emacs Modeline Support",
9887 description: "Number of lines to search for modelines (set to 0 to disable).",
9888 field: Box::new(SettingField {
9889 organization_override: None,
9890 json_path: Some("modeline_lines"),
9891 pick: |settings_content| settings_content.modeline_lines.as_ref(),
9892 write: |settings_content, value, _| {
9893 settings_content.modeline_lines = value;
9894 },
9895 }),
9896 metadata: None,
9897 files: USER | PROJECT,
9898 }),
9899 ]
9900 }
9901
9902 fn global_only_miscellaneous_sub_section() -> [SettingsPageItem; 3] {
9903 [
9904 SettingsPageItem::SettingItem(SettingItem {
9905 title: "Image Viewer",
9906 description: "The unit for image file sizes.",
9907 field: Box::new(SettingField {
9908 organization_override: None,
9909 json_path: Some("image_viewer.unit"),
9910 pick: |settings_content| {
9911 settings_content
9912 .image_viewer
9913 .as_ref()
9914 .and_then(|image_viewer| image_viewer.unit.as_ref())
9915 },
9916 write: |settings_content, value, _| {
9917 settings_content.image_viewer.get_or_insert_default().unit = value;
9918 },
9919 }),
9920 metadata: None,
9921 files: USER,
9922 }),
9923 SettingsPageItem::DynamicItem(DynamicItem {
9924 discriminant: SettingItem {
9925 files: USER,
9926 title: "Limit Markdown Preview Width",
9927 description: "Whether to constrain the markdown preview content to a maximum width, centering it when the pane is wider, for optimal readability.",
9928 field: Box::new(SettingField::<bool> {
9929 organization_override: None,
9930 json_path: Some("markdown_preview.limit_content_width"),
9931 pick: |settings_content| {
9932 settings_content
9933 .markdown_preview
9934 .as_ref()?
9935 .limit_content_width
9936 .as_ref()
9937 },
9938 write: |settings_content, value, _| {
9939 settings_content
9940 .markdown_preview
9941 .get_or_insert_default()
9942 .limit_content_width = value;
9943 },
9944 }),
9945 metadata: None,
9946 },
9947 pick_discriminant: |settings_content| {
9948 let enabled = settings_content
9949 .markdown_preview
9950 .as_ref()?
9951 .limit_content_width
9952 .unwrap_or(true);
9953 Some(if enabled { 1 } else { 0 })
9954 },
9955 fields: vec![
9956 vec![],
9957 vec![SettingItem {
9958 files: USER,
9959 title: "Max Width",
9960 description: "Maximum content width in pixels. Content will be centered when the pane is wider than this value.",
9961 field: Box::new(SettingField {
9962 organization_override: None,
9963 json_path: Some("markdown_preview.max_width"),
9964 pick: |settings_content| {
9965 settings_content
9966 .markdown_preview
9967 .as_ref()?
9968 .max_width
9969 .as_ref()
9970 },
9971 write: |settings_content, value, _| {
9972 settings_content
9973 .markdown_preview
9974 .get_or_insert_default()
9975 .max_width = value;
9976 },
9977 }),
9978 metadata: None,
9979 }],
9980 ],
9981 }),
9982 SettingsPageItem::SettingItem(SettingItem {
9983 title: "Drop Size Target",
9984 description: "Relative size of the drop target in the editor that will open dropped file as a split pane.",
9985 field: Box::new(SettingField {
9986 organization_override: None,
9987 json_path: Some("drop_target_size"),
9988 pick: |settings_content| settings_content.workspace.drop_target_size.as_ref(),
9989 write: |settings_content, value, _| {
9990 settings_content.workspace.drop_target_size = value;
9991 },
9992 }),
9993 metadata: None,
9994 files: USER,
9995 }),
9996 ]
9997 }
9998
9999 let is_global = active_language().is_none();
10000
10001 let code_lens_item = [SettingsPageItem::SettingItem(SettingItem {
10002 title: "Code Lens",
10003 description: "Whether and how to display code lenses from language servers.",
10004 field: Box::new(SettingField {
10005 organization_override: None,
10006 json_path: Some("code_lens"),
10007 pick: |settings_content| settings_content.editor.code_lens.as_ref(),
10008 write: |settings_content, value, _| {
10009 settings_content.editor.code_lens = value;
10010 },
10011 }),
10012 metadata: None,
10013 files: USER,
10014 })];
10015
10016 let lsp_document_colors_item = [SettingsPageItem::SettingItem(SettingItem {
10017 title: "LSP Document Colors",
10018 description: "How to render LSP color previews in the editor.",
10019 field: Box::new(SettingField {
10020 organization_override: None,
10021 json_path: Some("lsp_document_colors"),
10022 pick: |settings_content| settings_content.editor.lsp_document_colors.as_ref(),
10023 write: |settings_content, value, _| {
10024 settings_content.editor.lsp_document_colors = value;
10025 },
10026 }),
10027 metadata: None,
10028 files: USER,
10029 })];
10030
10031 if is_global {
10032 concat_sections!(
10033 indentation_section(),
10034 wrapping_section(),
10035 indent_guides_section(),
10036 formatting_section(),
10037 autoclose_section(),
10038 whitespace_section(),
10039 completions_section(),
10040 inlay_hints_section(),
10041 code_lens_item,
10042 lsp_document_colors_item,
10043 tasks_section(),
10044 miscellaneous_section(),
10045 global_only_miscellaneous_sub_section(),
10046 )
10047 } else {
10048 concat_sections!(
10049 indentation_section(),
10050 wrapping_section(),
10051 indent_guides_section(),
10052 formatting_section(),
10053 autoclose_section(),
10054 whitespace_section(),
10055 completions_section(),
10056 inlay_hints_section(),
10057 code_lens_item,
10058 tasks_section(),
10059 miscellaneous_section(),
10060 )
10061 }
10062}
10063
10064/// LanguageSettings items that should be included in the "Languages & Tools" page
10065/// not the "Editor" page
10066fn non_editor_language_settings_data() -> Box<[SettingsPageItem]> {
10067 fn lsp_section() -> [SettingsPageItem; 10] {
10068 [
10069 SettingsPageItem::SectionHeader("LSP"),
10070 SettingsPageItem::SettingItem(SettingItem {
10071 title: "Enable Language Server",
10072 description: "Whether to use language servers to provide code intelligence.",
10073 field: Box::new(SettingField {
10074 organization_override: None,
10075 json_path: Some("languages.$(language).enable_language_server"),
10076 pick: |settings_content| {
10077 language_settings_field(settings_content, |language| {
10078 language.enable_language_server.as_ref()
10079 })
10080 },
10081 write: |settings_content, value, _| {
10082 language_settings_field_mut(settings_content, value, |language, value| {
10083 language.enable_language_server = value;
10084 })
10085 },
10086 }),
10087 metadata: None,
10088 files: USER | PROJECT,
10089 }),
10090 SettingsPageItem::SettingItem(SettingItem {
10091 title: "Language Servers",
10092 description: "The list of language servers to use (or disable) for this language.",
10093 field: Box::new(
10094 SettingField {
10095 organization_override: None,
10096 json_path: Some("languages.$(language).language_servers"),
10097 pick: |settings_content| {
10098 language_settings_field(settings_content, |language| {
10099 language.language_servers.as_ref()
10100 })
10101 },
10102 write: |settings_content, value, _| {
10103 language_settings_field_mut(
10104 settings_content,
10105 value,
10106 |language, value| {
10107 language.language_servers = value;
10108 },
10109 )
10110 },
10111 }
10112 .unimplemented(),
10113 ),
10114 metadata: None,
10115 files: USER | PROJECT,
10116 }),
10117 SettingsPageItem::SettingItem(SettingItem {
10118 title: "Linked Edits",
10119 description: "Whether to perform linked edits of associated ranges, if the LS supports it. For example, when editing opening <html> tag, the contents of the closing </html> tag will be edited as well.",
10120 field: Box::new(SettingField {
10121 organization_override: None,
10122 json_path: Some("languages.$(language).linked_edits"),
10123 pick: |settings_content| {
10124 language_settings_field(settings_content, |language| {
10125 language.linked_edits.as_ref()
10126 })
10127 },
10128 write: |settings_content, value, _| {
10129 language_settings_field_mut(settings_content, value, |language, value| {
10130 language.linked_edits = value;
10131 })
10132 },
10133 }),
10134 metadata: None,
10135 files: USER | PROJECT,
10136 }),
10137 SettingsPageItem::SettingItem(SettingItem {
10138 title: "Go To Definition Fallback",
10139 description: "Whether to follow-up empty Go to definition responses from the language server.",
10140 field: Box::new(SettingField {
10141 organization_override: None,
10142 json_path: Some("go_to_definition_fallback"),
10143 pick: |settings_content| {
10144 settings_content.editor.go_to_definition_fallback.as_ref()
10145 },
10146 write: |settings_content, value, _| {
10147 settings_content.editor.go_to_definition_fallback = value;
10148 },
10149 }),
10150 metadata: None,
10151 files: USER,
10152 }),
10153 SettingsPageItem::SettingItem(SettingItem {
10154 title: "Go To Definition Scroll Strategy",
10155 description: "How to scroll the target into view when navigating to a definition or reference.",
10156 field: Box::new(SettingField {
10157 organization_override: None,
10158 json_path: Some("go_to_definition_scroll_strategy"),
10159 pick: |settings_content| {
10160 settings_content
10161 .editor
10162 .go_to_definition_scroll_strategy
10163 .as_ref()
10164 },
10165 write: |settings_content, value, _| {
10166 settings_content.editor.go_to_definition_scroll_strategy = value;
10167 },
10168 }),
10169 metadata: None,
10170 files: USER,
10171 }),
10172 SettingsPageItem::SettingItem(SettingItem {
10173 title: "LSP Results Location",
10174 description: "Where to show LSP results that can contain multiple locations (Go to Definition, Go to Implementation, Find All References).",
10175 field: Box::new(SettingField {
10176 organization_override: None,
10177 json_path: Some("lsp_results_location"),
10178 pick: |settings_content| settings_content.editor.lsp_results_location.as_ref(),
10179 write: |settings_content, value, _| {
10180 settings_content.editor.lsp_results_location = value;
10181 },
10182 }),
10183 metadata: None,
10184 files: USER,
10185 }),
10186 SettingsPageItem::SettingItem(SettingItem {
10187 title: "Semantic Tokens",
10188 description: {
10189 static DESCRIPTION: OnceLock<&'static str> = OnceLock::new();
10190 DESCRIPTION.get_or_init(|| {
10191 SemanticTokens::VARIANTS
10192 .iter()
10193 .filter_map(|v| {
10194 v.get_documentation().map(|doc| format!("{v:?}: {doc}"))
10195 })
10196 .join("\n")
10197 .leak()
10198 })
10199 },
10200 field: Box::new(SettingField {
10201 organization_override: None,
10202 json_path: Some("languages.$(language).semantic_tokens"),
10203 pick: |settings_content| {
10204 settings_content
10205 .project
10206 .all_languages
10207 .defaults
10208 .semantic_tokens
10209 .as_ref()
10210 },
10211 write: |settings_content, value, _| {
10212 settings_content
10213 .project
10214 .all_languages
10215 .defaults
10216 .semantic_tokens = value;
10217 },
10218 }),
10219 metadata: None,
10220 files: USER | PROJECT,
10221 }),
10222 SettingsPageItem::SettingItem(SettingItem {
10223 title: "LSP Folding Ranges",
10224 description: "When enabled, use folding ranges from the language server instead of indent-based folding.",
10225 field: Box::new(SettingField {
10226 organization_override: None,
10227 json_path: Some("languages.$(language).document_folding_ranges"),
10228 pick: |settings_content| {
10229 language_settings_field(settings_content, |language| {
10230 language.document_folding_ranges.as_ref()
10231 })
10232 },
10233 write: |settings_content, value, _| {
10234 language_settings_field_mut(settings_content, value, |language, value| {
10235 language.document_folding_ranges = value;
10236 })
10237 },
10238 }),
10239 metadata: None,
10240 files: USER | PROJECT,
10241 }),
10242 SettingsPageItem::SettingItem(SettingItem {
10243 title: "LSP Document Symbols",
10244 description: "When enabled, use the language server's document symbols for outlines and breadcrumbs instead of tree-sitter.",
10245 field: Box::new(SettingField {
10246 organization_override: None,
10247 json_path: Some("languages.$(language).document_symbols"),
10248 pick: |settings_content| {
10249 language_settings_field(settings_content, |language| {
10250 language.document_symbols.as_ref()
10251 })
10252 },
10253 write: |settings_content, value, _| {
10254 language_settings_field_mut(settings_content, value, |language, value| {
10255 language.document_symbols = value;
10256 })
10257 },
10258 }),
10259 metadata: None,
10260 files: USER | PROJECT,
10261 }),
10262 ]
10263 }
10264
10265 fn lsp_completions_section() -> [SettingsPageItem; 4] {
10266 [
10267 SettingsPageItem::SectionHeader("LSP Completions"),
10268 SettingsPageItem::SettingItem(SettingItem {
10269 title: "Enabled",
10270 description: "Whether to fetch LSP completions or not.",
10271 field: Box::new(SettingField {
10272 organization_override: None,
10273 json_path: Some("languages.$(language).completions.lsp"),
10274 pick: |settings_content| {
10275 language_settings_field(settings_content, |language| {
10276 language.completions.as_ref()?.lsp.as_ref()
10277 })
10278 },
10279 write: |settings_content, value, _| {
10280 language_settings_field_mut(settings_content, value, |language, value| {
10281 language.completions.get_or_insert_default().lsp = value;
10282 })
10283 },
10284 }),
10285 metadata: None,
10286 files: USER | PROJECT,
10287 }),
10288 SettingsPageItem::SettingItem(SettingItem {
10289 title: "Fetch Timeout (milliseconds)",
10290 description: "When fetching LSP completions, determines how long to wait for a response of a particular server (set to 0 to wait indefinitely).",
10291 field: Box::new(SettingField {
10292 organization_override: None,
10293 json_path: Some("languages.$(language).completions.lsp_fetch_timeout_ms"),
10294 pick: |settings_content| {
10295 language_settings_field(settings_content, |language| {
10296 language.completions.as_ref()?.lsp_fetch_timeout_ms.as_ref()
10297 })
10298 },
10299 write: |settings_content, value, _| {
10300 language_settings_field_mut(settings_content, value, |language, value| {
10301 language
10302 .completions
10303 .get_or_insert_default()
10304 .lsp_fetch_timeout_ms = value;
10305 })
10306 },
10307 }),
10308 metadata: None,
10309 files: USER | PROJECT,
10310 }),
10311 SettingsPageItem::SettingItem(SettingItem {
10312 title: "Insert Mode",
10313 description: "Controls how LSP completions are inserted.",
10314 field: Box::new(SettingField {
10315 organization_override: None,
10316 json_path: Some("languages.$(language).completions.lsp_insert_mode"),
10317 pick: |settings_content| {
10318 language_settings_field(settings_content, |language| {
10319 language.completions.as_ref()?.lsp_insert_mode.as_ref()
10320 })
10321 },
10322 write: |settings_content, value, _| {
10323 language_settings_field_mut(settings_content, value, |language, value| {
10324 language.completions.get_or_insert_default().lsp_insert_mode = value;
10325 })
10326 },
10327 }),
10328 metadata: None,
10329 files: USER | PROJECT,
10330 }),
10331 ]
10332 }
10333
10334 fn debugger_section() -> [SettingsPageItem; 2] {
10335 [
10336 SettingsPageItem::SectionHeader("Debuggers"),
10337 SettingsPageItem::SettingItem(SettingItem {
10338 title: "Debuggers",
10339 description: "Preferred debuggers for this language.",
10340 field: Box::new(
10341 SettingField {
10342 organization_override: None,
10343 json_path: Some("languages.$(language).debuggers"),
10344 pick: |settings_content| {
10345 language_settings_field(settings_content, |language| {
10346 language.debuggers.as_ref()
10347 })
10348 },
10349 write: |settings_content, value, _| {
10350 language_settings_field_mut(
10351 settings_content,
10352 value,
10353 |language, value| {
10354 language.debuggers = value;
10355 },
10356 )
10357 },
10358 }
10359 .unimplemented(),
10360 ),
10361 metadata: None,
10362 files: USER | PROJECT,
10363 }),
10364 ]
10365 }
10366
10367 fn prettier_section() -> [SettingsPageItem; 5] {
10368 [
10369 SettingsPageItem::SectionHeader("Prettier"),
10370 SettingsPageItem::SettingItem(SettingItem {
10371 title: "Allowed",
10372 description: "Enables or disables formatting with Prettier for a given language.",
10373 field: Box::new(SettingField {
10374 organization_override: None,
10375 json_path: Some("languages.$(language).prettier.allowed"),
10376 pick: |settings_content| {
10377 language_settings_field(settings_content, |language| {
10378 language.prettier.as_ref()?.allowed.as_ref()
10379 })
10380 },
10381 write: |settings_content, value, _| {
10382 language_settings_field_mut(settings_content, value, |language, value| {
10383 language.prettier.get_or_insert_default().allowed = value;
10384 })
10385 },
10386 }),
10387 metadata: None,
10388 files: USER | PROJECT,
10389 }),
10390 SettingsPageItem::SettingItem(SettingItem {
10391 title: "Parser",
10392 description: "Forces Prettier integration to use a specific parser name when formatting files with the language.",
10393 field: Box::new(SettingField {
10394 organization_override: None,
10395 json_path: Some("languages.$(language).prettier.parser"),
10396 pick: |settings_content| {
10397 language_settings_field(settings_content, |language| {
10398 language.prettier.as_ref()?.parser.as_ref()
10399 })
10400 },
10401 write: |settings_content, value, _| {
10402 language_settings_field_mut(settings_content, value, |language, value| {
10403 language.prettier.get_or_insert_default().parser = value;
10404 })
10405 },
10406 }),
10407 metadata: None,
10408 files: USER | PROJECT,
10409 }),
10410 SettingsPageItem::SettingItem(SettingItem {
10411 title: "Plugins",
10412 description: "Forces Prettier integration to use specific plugins when formatting files with the language.",
10413 field: Box::new(
10414 SettingField {
10415 organization_override: None,
10416 json_path: Some("languages.$(language).prettier.plugins"),
10417 pick: |settings_content| {
10418 language_settings_field(settings_content, |language| {
10419 language.prettier.as_ref()?.plugins.as_ref()
10420 })
10421 },
10422 write: |settings_content, value, _| {
10423 language_settings_field_mut(
10424 settings_content,
10425 value,
10426 |language, value| {
10427 language.prettier.get_or_insert_default().plugins = value;
10428 },
10429 )
10430 },
10431 }
10432 .unimplemented(),
10433 ),
10434 metadata: None,
10435 files: USER | PROJECT,
10436 }),
10437 SettingsPageItem::SettingItem(SettingItem {
10438 title: "Options",
10439 description: "Default Prettier options, in the format as in package.json section for Prettier.",
10440 field: Box::new(
10441 SettingField {
10442 organization_override: None,
10443 json_path: Some("languages.$(language).prettier.options"),
10444 pick: |settings_content| {
10445 language_settings_field(settings_content, |language| {
10446 language.prettier.as_ref()?.options.as_ref()
10447 })
10448 },
10449 write: |settings_content, value, _| {
10450 language_settings_field_mut(
10451 settings_content,
10452 value,
10453 |language, value| {
10454 language.prettier.get_or_insert_default().options = value;
10455 },
10456 )
10457 },
10458 }
10459 .unimplemented(),
10460 ),
10461 metadata: None,
10462 files: USER | PROJECT,
10463 }),
10464 ]
10465 }
10466
10467 concat_sections!(
10468 lsp_section(),
10469 lsp_completions_section(),
10470 debugger_section(),
10471 prettier_section(),
10472 )
10473}
10474
10475fn edit_prediction_language_settings_section() -> [SettingsPageItem; 5] {
10476 [
10477 SettingsPageItem::SectionHeader("Edit Predictions"),
10478 SettingsPageItem::SubPageLink(SubPageLink {
10479 title: "Configure Providers".into(),
10480 r#type: Default::default(),
10481 json_path: Some("edit_predictions.providers"),
10482 description: Some("Set up different edit prediction providers in complement to the built-in edit prediction model (unavailable in Omega by default).".into()),
10483 search_aliases: &[],
10484 in_json: false,
10485 files: USER,
10486 render: render_edit_prediction_setup_page
10487 }),
10488 SettingsPageItem::SettingItem(SettingItem {
10489 title: "Data Collection",
10490 description: "Controls whether Omega may collect training data when using built-in edit predictions. Data is only collected for files in projects detected as open source. The default value uses the preference previously set via the status-bar toggle, or false if no preference has been stored.",
10491 field: Box::new(SettingField {
10492 organization_override: Some(|org_settings| {
10493 const DATA_COLLECTION_DISABLED: EditPredictionDataCollectionChoice = EditPredictionDataCollectionChoice::No;
10494
10495 if !org_settings.edit_prediction.is_feedback_enabled {
10496 Some(&DATA_COLLECTION_DISABLED)
10497 } else {
10498 None
10499 }
10500 }),
10501 json_path: Some("edit_predictions.allow_data_collection"),
10502 pick: |settings_content| {
10503 settings_content
10504 .project
10505 .all_languages
10506 .edit_predictions
10507 .as_ref()?
10508 .allow_data_collection
10509 .as_ref()
10510 },
10511 write: |settings_content, value, _app| {
10512 settings_content
10513 .project
10514 .all_languages
10515 .edit_predictions
10516 .get_or_insert_default()
10517 .allow_data_collection = value;
10518 },
10519 }),
10520 metadata: None,
10521 files: USER,
10522 }),
10523 SettingsPageItem::SettingItem(SettingItem {
10524 title: "Show Edit Predictions",
10525 description: "Controls whether edit predictions are shown immediately or manually.",
10526 field: Box::new(SettingField {
10527 organization_override: None,
10528 json_path: Some("languages.$(language).show_edit_predictions"),
10529 pick: |settings_content| {
10530 language_settings_field(settings_content, |language| {
10531 language.show_edit_predictions.as_ref()
10532 })
10533 },
10534 write: |settings_content, value, _| {
10535 language_settings_field_mut(settings_content, value, |language, value| {
10536 language.show_edit_predictions = value;
10537 })
10538 },
10539 }),
10540 metadata: None,
10541 files: USER | PROJECT,
10542 }),
10543 SettingsPageItem::SettingItem(SettingItem {
10544 title: "Disable in Language Scopes",
10545 description: "Controls whether edit predictions are shown in the given language scopes.",
10546 field: Box::new(
10547 SettingField {
10548 organization_override: None,
10549 json_path: Some("languages.$(language).edit_predictions_disabled_in"),
10550 pick: |settings_content| {
10551 language_settings_field(settings_content, |language| {
10552 language.edit_predictions_disabled_in.as_ref()
10553 })
10554 },
10555 write: |settings_content, value, _| {
10556 language_settings_field_mut(settings_content, value, |language, value| {
10557 language.edit_predictions_disabled_in = value;
10558 })
10559 },
10560 }
10561 .unimplemented(),
10562 ),
10563 metadata: None,
10564 files: USER | PROJECT,
10565 }),
10566 ]
10567}
10568
10569fn show_scrollbar_or_editor(
10570 settings_content: &SettingsContent,
10571 show: fn(&SettingsContent) -> Option<&settings::ShowScrollbar>,
10572) -> Option<&settings::ShowScrollbar> {
10573 show(settings_content).or(settings_content
10574 .editor
10575 .scrollbar
10576 .as_ref()
10577 .and_then(|scrollbar| scrollbar.show.as_ref()))
10578}
10579
10580fn dynamic_variants<T>() -> &'static [T::Discriminant]
10581where
10582 T: strum::IntoDiscriminant,
10583 T::Discriminant: strum::VariantArray,
10584{
10585 <<T as strum::IntoDiscriminant>::Discriminant as strum::VariantArray>::VARIANTS
10586}
10587
10588/// Updates the `vim_mode` setting, disabling `helix_mode` if present and
10589/// `vim_mode` is being enabled.
10590fn write_vim_mode(settings: &mut SettingsContent, value: Option<bool>, _: &App) {
10591 write_vim_mode_inner(settings, value);
10592}
10593
10594fn write_vim_mode_inner(settings: &mut SettingsContent, value: Option<bool>) {
10595 if value == Some(true) && settings.helix_mode == Some(true) {
10596 settings.helix_mode = Some(false);
10597 }
10598 settings.vim_mode = value;
10599}
10600
10601/// Updates the `helix_mode` setting, disabling `vim_mode` if present and
10602/// `helix_mode` is being enabled.
10603fn write_helix_mode(settings: &mut SettingsContent, value: Option<bool>, _: &App) {
10604 write_helix_mode_inner(settings, value);
10605}
10606
10607fn write_helix_mode_inner(settings: &mut SettingsContent, value: Option<bool>) {
10608 if value == Some(true) && settings.vim_mode == Some(true) {
10609 settings.vim_mode = Some(false);
10610 }
10611 settings.helix_mode = value;
10612}
10613
10614#[cfg(test)]
10615mod tests {
10616 use super::*;
10617
10618 #[test]
10619 fn test_write_vim_helix_mode() {
10620 // Enabling vim mode while `vim_mode` and `helix_mode` are not yet set
10621 // should only update the `vim_mode` setting.
10622 let mut settings = SettingsContent::default();
10623 write_vim_mode_inner(&mut settings, Some(true));
10624 assert_eq!(settings.vim_mode, Some(true));
10625 assert_eq!(settings.helix_mode, None);
10626
10627 // Enabling helix mode while `vim_mode` and `helix_mode` are not yet set
10628 // should only update the `helix_mode` setting.
10629 let mut settings = SettingsContent::default();
10630 write_helix_mode_inner(&mut settings, Some(true));
10631 assert_eq!(settings.helix_mode, Some(true));
10632 assert_eq!(settings.vim_mode, None);
10633
10634 // Disabling helix mode should only touch `helix_mode` setting when
10635 // `vim_mode` is not set.
10636 write_helix_mode_inner(&mut settings, Some(false));
10637 assert_eq!(settings.helix_mode, Some(false));
10638 assert_eq!(settings.vim_mode, None);
10639
10640 // Enabling vim mode should update `vim_mode` but leave `helix_mode`
10641 // untouched.
10642 write_vim_mode_inner(&mut settings, Some(true));
10643 assert_eq!(settings.vim_mode, Some(true));
10644 assert_eq!(settings.helix_mode, Some(false));
10645
10646 // Enabling helix mode should update `helix_mode` and disable
10647 // `vim_mode`.
10648 write_helix_mode_inner(&mut settings, Some(true));
10649 assert_eq!(settings.helix_mode, Some(true));
10650 assert_eq!(settings.vim_mode, Some(false));
10651
10652 // Enabling vim mode should update `vim_mode` and disable
10653 // `helix_mode`.
10654 write_vim_mode_inner(&mut settings, Some(true));
10655 assert_eq!(settings.vim_mode, Some(true));
10656 assert_eq!(settings.helix_mode, Some(false));
10657 }
10658}
10659