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