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