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 fn vim_settings_section() -> [SettingsPageItem; 11] {
2418 [
2419 SettingsPageItem::SectionHeader("Vim"),
2420 SettingsPageItem::SettingItem(SettingItem {
2421 title: "Default Mode",
2422 description: "The default mode when Vim starts.",
2423 field: Box::new(SettingField {
2424 json_path: Some("vim.default_mode"),
2425 pick: |settings_content| settings_content.vim.as_ref()?.default_mode.as_ref(),
2426 write: |settings_content, value| {
2427 settings_content.vim.get_or_insert_default().default_mode = value;
2428 },
2429 }),
2430 metadata: None,
2431 files: USER,
2432 }),
2433 SettingsPageItem::SettingItem(SettingItem {
2434 title: "Toggle Relative Line Numbers",
2435 description: "Toggle relative line numbers in Vim mode.",
2436 field: Box::new(SettingField {
2437 json_path: Some("vim.toggle_relative_line_numbers"),
2438 pick: |settings_content| {
2439 settings_content
2440 .vim
2441 .as_ref()?
2442 .toggle_relative_line_numbers
2443 .as_ref()
2444 },
2445 write: |settings_content, value| {
2446 settings_content
2447 .vim
2448 .get_or_insert_default()
2449 .toggle_relative_line_numbers = value;
2450 },
2451 }),
2452 metadata: None,
2453 files: USER,
2454 }),
2455 SettingsPageItem::SettingItem(SettingItem {
2456 title: "Use System Clipboard",
2457 description: "Controls when to use system clipboard in Vim mode.",
2458 field: Box::new(SettingField {
2459 json_path: Some("vim.use_system_clipboard"),
2460 pick: |settings_content| {
2461 settings_content.vim.as_ref()?.use_system_clipboard.as_ref()
2462 },
2463 write: |settings_content, value| {
2464 settings_content
2465 .vim
2466 .get_or_insert_default()
2467 .use_system_clipboard = value;
2468 },
2469 }),
2470 metadata: None,
2471 files: USER,
2472 }),
2473 SettingsPageItem::SettingItem(SettingItem {
2474 title: "Use Smartcase Find",
2475 description: "Enable smartcase searching in Vim mode.",
2476 field: Box::new(SettingField {
2477 json_path: Some("vim.use_smartcase_find"),
2478 pick: |settings_content| {
2479 settings_content.vim.as_ref()?.use_smartcase_find.as_ref()
2480 },
2481 write: |settings_content, value| {
2482 settings_content
2483 .vim
2484 .get_or_insert_default()
2485 .use_smartcase_find = value;
2486 },
2487 }),
2488 metadata: None,
2489 files: USER,
2490 }),
2491 SettingsPageItem::SettingItem(SettingItem {
2492 title: "Highlight on Yank Duration",
2493 description: "Duration in milliseconds to highlight yanked text in Vim mode.",
2494 field: Box::new(SettingField {
2495 json_path: Some("vim.highlight_on_yank_duration"),
2496 pick: |settings_content| {
2497 settings_content
2498 .vim
2499 .as_ref()?
2500 .highlight_on_yank_duration
2501 .as_ref()
2502 },
2503 write: |settings_content, value| {
2504 settings_content
2505 .vim
2506 .get_or_insert_default()
2507 .highlight_on_yank_duration = value;
2508 },
2509 }),
2510 metadata: None,
2511 files: USER,
2512 }),
2513 SettingsPageItem::SettingItem(SettingItem {
2514 title: "Cursor Shape - Normal Mode",
2515 description: "Cursor shape for normal mode.",
2516 field: Box::new(SettingField {
2517 json_path: Some("vim.cursor_shape.normal"),
2518 pick: |settings_content| {
2519 settings_content
2520 .vim
2521 .as_ref()?
2522 .cursor_shape
2523 .as_ref()?
2524 .normal
2525 .as_ref()
2526 },
2527 write: |settings_content, value| {
2528 settings_content
2529 .vim
2530 .get_or_insert_default()
2531 .cursor_shape
2532 .get_or_insert_default()
2533 .normal = value;
2534 },
2535 }),
2536 metadata: None,
2537 files: USER,
2538 }),
2539 SettingsPageItem::SettingItem(SettingItem {
2540 title: "Cursor Shape - Insert Mode",
2541 description: "Cursor shape for insert mode. Inherit uses the editor's cursor shape.",
2542 field: Box::new(SettingField {
2543 json_path: Some("vim.cursor_shape.insert"),
2544 pick: |settings_content| {
2545 settings_content
2546 .vim
2547 .as_ref()?
2548 .cursor_shape
2549 .as_ref()?
2550 .insert
2551 .as_ref()
2552 },
2553 write: |settings_content, value| {
2554 settings_content
2555 .vim
2556 .get_or_insert_default()
2557 .cursor_shape
2558 .get_or_insert_default()
2559 .insert = value;
2560 },
2561 }),
2562 metadata: None,
2563 files: USER,
2564 }),
2565 SettingsPageItem::SettingItem(SettingItem {
2566 title: "Cursor Shape - Replace Mode",
2567 description: "Cursor shape for replace mode.",
2568 field: Box::new(SettingField {
2569 json_path: Some("vim.cursor_shape.replace"),
2570 pick: |settings_content| {
2571 settings_content
2572 .vim
2573 .as_ref()?
2574 .cursor_shape
2575 .as_ref()?
2576 .replace
2577 .as_ref()
2578 },
2579 write: |settings_content, value| {
2580 settings_content
2581 .vim
2582 .get_or_insert_default()
2583 .cursor_shape
2584 .get_or_insert_default()
2585 .replace = value;
2586 },
2587 }),
2588 metadata: None,
2589 files: USER,
2590 }),
2591 SettingsPageItem::SettingItem(SettingItem {
2592 title: "Cursor Shape - Visual Mode",
2593 description: "Cursor shape for visual mode.",
2594 field: Box::new(SettingField {
2595 json_path: Some("vim.cursor_shape.visual"),
2596 pick: |settings_content| {
2597 settings_content
2598 .vim
2599 .as_ref()?
2600 .cursor_shape
2601 .as_ref()?
2602 .visual
2603 .as_ref()
2604 },
2605 write: |settings_content, value| {
2606 settings_content
2607 .vim
2608 .get_or_insert_default()
2609 .cursor_shape
2610 .get_or_insert_default()
2611 .visual = value;
2612 },
2613 }),
2614 metadata: None,
2615 files: USER,
2616 }),
2617 SettingsPageItem::SettingItem(SettingItem {
2618 title: "Custom Digraphs",
2619 description: "Custom digraph mappings for Vim mode.",
2620 field: Box::new(
2621 SettingField {
2622 json_path: Some("vim.custom_digraphs"),
2623 pick: |settings_content| {
2624 settings_content.vim.as_ref()?.custom_digraphs.as_ref()
2625 },
2626 write: |settings_content, value| {
2627 settings_content.vim.get_or_insert_default().custom_digraphs = value;
2628 },
2629 }
2630 .unimplemented(),
2631 ),
2632 metadata: None,
2633 files: USER,
2634 }),
2635 ]
2636 }
2637
2638 let items = concat_sections!(
2639 auto_save_section(),
2640 which_key_section(),
2641 multibuffer_section(),
2642 scrolling_section(),
2643 signature_help_section(),
2644 hover_popover_section(),
2645 drag_and_drop_selection_section(),
2646 gutter_section(),
2647 scrollbar_section(),
2648 minimap_section(),
2649 toolbar_section(),
2650 vim_settings_section(),
2651 language_settings_data(),
2652 );
2653
2654 SettingsPage {
2655 title: "Editor",
2656 items: items,
2657 }
2658}
2659
2660fn languages_and_tools_page(cx: &App) -> SettingsPage {
2661 fn file_types_section() -> [SettingsPageItem; 2] {
2662 [
2663 SettingsPageItem::SectionHeader("File Types"),
2664 SettingsPageItem::SettingItem(SettingItem {
2665 title: "File Type Associations",
2666 description: "A mapping from languages to files and file extensions that should be treated as that language.",
2667 field: Box::new(
2668 SettingField {
2669 json_path: Some("file_type_associations"),
2670 pick: |settings_content| {
2671 settings_content.project.all_languages.file_types.as_ref()
2672 },
2673 write: |settings_content, value| {
2674 settings_content.project.all_languages.file_types = value;
2675 },
2676 }
2677 .unimplemented(),
2678 ),
2679 metadata: None,
2680 files: USER | PROJECT,
2681 }),
2682 ]
2683 }
2684
2685 fn diagnostics_section() -> [SettingsPageItem; 3] {
2686 [
2687 SettingsPageItem::SectionHeader("Diagnostics"),
2688 SettingsPageItem::SettingItem(SettingItem {
2689 title: "Max Severity",
2690 description: "Which level to use to filter out diagnostics displayed in the editor.",
2691 field: Box::new(SettingField {
2692 json_path: Some("diagnostics_max_severity"),
2693 pick: |settings_content| {
2694 settings_content.editor.diagnostics_max_severity.as_ref()
2695 },
2696 write: |settings_content, value| {
2697 settings_content.editor.diagnostics_max_severity = value;
2698 },
2699 }),
2700 metadata: None,
2701 files: USER,
2702 }),
2703 SettingsPageItem::SettingItem(SettingItem {
2704 title: "Include Warnings",
2705 description: "Whether to show warnings or not by default.",
2706 field: Box::new(SettingField {
2707 json_path: Some("diagnostics.include_warnings"),
2708 pick: |settings_content| {
2709 settings_content
2710 .diagnostics
2711 .as_ref()?
2712 .include_warnings
2713 .as_ref()
2714 },
2715 write: |settings_content, value| {
2716 settings_content
2717 .diagnostics
2718 .get_or_insert_default()
2719 .include_warnings = value;
2720 },
2721 }),
2722 metadata: None,
2723 files: USER,
2724 }),
2725 ]
2726 }
2727
2728 fn inline_diagnostics_section() -> [SettingsPageItem; 5] {
2729 [
2730 SettingsPageItem::SectionHeader("Inline Diagnostics"),
2731 SettingsPageItem::SettingItem(SettingItem {
2732 title: "Enabled",
2733 description: "Whether to show diagnostics inline or not.",
2734 field: Box::new(SettingField {
2735 json_path: Some("diagnostics.inline.enabled"),
2736 pick: |settings_content| {
2737 settings_content
2738 .diagnostics
2739 .as_ref()?
2740 .inline
2741 .as_ref()?
2742 .enabled
2743 .as_ref()
2744 },
2745 write: |settings_content, value| {
2746 settings_content
2747 .diagnostics
2748 .get_or_insert_default()
2749 .inline
2750 .get_or_insert_default()
2751 .enabled = value;
2752 },
2753 }),
2754 metadata: None,
2755 files: USER,
2756 }),
2757 SettingsPageItem::SettingItem(SettingItem {
2758 title: "Update Debounce",
2759 description: "The delay in milliseconds to show inline diagnostics after the last diagnostic update.",
2760 field: Box::new(SettingField {
2761 json_path: Some("diagnostics.inline.update_debounce_ms"),
2762 pick: |settings_content| {
2763 settings_content
2764 .diagnostics
2765 .as_ref()?
2766 .inline
2767 .as_ref()?
2768 .update_debounce_ms
2769 .as_ref()
2770 },
2771 write: |settings_content, value| {
2772 settings_content
2773 .diagnostics
2774 .get_or_insert_default()
2775 .inline
2776 .get_or_insert_default()
2777 .update_debounce_ms = value;
2778 },
2779 }),
2780 metadata: None,
2781 files: USER,
2782 }),
2783 SettingsPageItem::SettingItem(SettingItem {
2784 title: "Padding",
2785 description: "The amount of padding between the end of the source line and the start of the inline diagnostic.",
2786 field: Box::new(SettingField {
2787 json_path: Some("diagnostics.inline.padding"),
2788 pick: |settings_content| {
2789 settings_content
2790 .diagnostics
2791 .as_ref()?
2792 .inline
2793 .as_ref()?
2794 .padding
2795 .as_ref()
2796 },
2797 write: |settings_content, value| {
2798 settings_content
2799 .diagnostics
2800 .get_or_insert_default()
2801 .inline
2802 .get_or_insert_default()
2803 .padding = value;
2804 },
2805 }),
2806 metadata: None,
2807 files: USER,
2808 }),
2809 SettingsPageItem::SettingItem(SettingItem {
2810 title: "Minimum Column",
2811 description: "The minimum column at which to display inline diagnostics.",
2812 field: Box::new(SettingField {
2813 json_path: Some("diagnostics.inline.min_column"),
2814 pick: |settings_content| {
2815 settings_content
2816 .diagnostics
2817 .as_ref()?
2818 .inline
2819 .as_ref()?
2820 .min_column
2821 .as_ref()
2822 },
2823 write: |settings_content, value| {
2824 settings_content
2825 .diagnostics
2826 .get_or_insert_default()
2827 .inline
2828 .get_or_insert_default()
2829 .min_column = value;
2830 },
2831 }),
2832 metadata: None,
2833 files: USER,
2834 }),
2835 ]
2836 }
2837
2838 fn lsp_pull_diagnostics_section() -> [SettingsPageItem; 3] {
2839 [
2840 SettingsPageItem::SectionHeader("LSP Pull Diagnostics"),
2841 SettingsPageItem::SettingItem(SettingItem {
2842 title: "Enabled",
2843 description: "Whether to pull for language server-powered diagnostics or not.",
2844 field: Box::new(SettingField {
2845 json_path: Some("diagnostics.lsp_pull_diagnostics.enabled"),
2846 pick: |settings_content| {
2847 settings_content
2848 .diagnostics
2849 .as_ref()?
2850 .lsp_pull_diagnostics
2851 .as_ref()?
2852 .enabled
2853 .as_ref()
2854 },
2855 write: |settings_content, value| {
2856 settings_content
2857 .diagnostics
2858 .get_or_insert_default()
2859 .lsp_pull_diagnostics
2860 .get_or_insert_default()
2861 .enabled = value;
2862 },
2863 }),
2864 metadata: None,
2865 files: USER,
2866 }),
2867 // todo(settings_ui): Needs unit
2868 SettingsPageItem::SettingItem(SettingItem {
2869 title: "Debounce",
2870 description: "Minimum time to wait before pulling diagnostics from the language server(s).",
2871 field: Box::new(SettingField {
2872 json_path: Some("diagnostics.lsp_pull_diagnostics.debounce_ms"),
2873 pick: |settings_content| {
2874 settings_content
2875 .diagnostics
2876 .as_ref()?
2877 .lsp_pull_diagnostics
2878 .as_ref()?
2879 .debounce_ms
2880 .as_ref()
2881 },
2882 write: |settings_content, value| {
2883 settings_content
2884 .diagnostics
2885 .get_or_insert_default()
2886 .lsp_pull_diagnostics
2887 .get_or_insert_default()
2888 .debounce_ms = value;
2889 },
2890 }),
2891 metadata: None,
2892 files: USER,
2893 }),
2894 ]
2895 }
2896
2897 fn lsp_highlights_section() -> [SettingsPageItem; 2] {
2898 [
2899 SettingsPageItem::SectionHeader("LSP Highlights"),
2900 SettingsPageItem::SettingItem(SettingItem {
2901 title: "Debounce",
2902 description: "The debounce delay before querying highlights from the language.",
2903 field: Box::new(SettingField {
2904 json_path: Some("lsp_highlight_debounce"),
2905 pick: |settings_content| {
2906 settings_content.editor.lsp_highlight_debounce.as_ref()
2907 },
2908 write: |settings_content, value| {
2909 settings_content.editor.lsp_highlight_debounce = value;
2910 },
2911 }),
2912 metadata: None,
2913 files: USER,
2914 }),
2915 ]
2916 }
2917
2918 fn languages_list_section(cx: &App) -> Box<[SettingsPageItem]> {
2919 // todo(settings_ui): Refresh on extension (un)/installed
2920 // Note that `crates/json_schema_store` solves the same problem, there is probably a way to unify the two
2921 std::iter::once(SettingsPageItem::SectionHeader(LANGUAGES_SECTION_HEADER))
2922 .chain(all_language_names(cx).into_iter().map(|language_name| {
2923 let link = format!("languages.{language_name}");
2924 SettingsPageItem::SubPageLink(SubPageLink {
2925 title: language_name,
2926 description: None,
2927 json_path: Some(link.leak()),
2928 in_json: true,
2929 files: USER | PROJECT,
2930 render: Arc::new(|this, window, cx| {
2931 let items: Box<[SettingsPageItem]> = concat_sections!(
2932 language_settings_data(),
2933 non_editor_language_settings_data(),
2934 edit_prediction_language_settings_section()
2935 );
2936 this.render_sub_page_items(items.iter().enumerate(), None, window, cx)
2937 .into_any_element()
2938 }),
2939 })
2940 }))
2941 .collect()
2942 }
2943
2944 SettingsPage {
2945 title: "Languages & Tools",
2946 items: {
2947 concat_sections!(
2948 non_editor_language_settings_data(),
2949 file_types_section(),
2950 diagnostics_section(),
2951 inline_diagnostics_section(),
2952 lsp_pull_diagnostics_section(),
2953 lsp_highlights_section(),
2954 languages_list_section(cx),
2955 )
2956 },
2957 }
2958}
2959
2960fn search_and_files_page() -> SettingsPage {
2961 fn search_section() -> [SettingsPageItem; 9] {
2962 [
2963 SettingsPageItem::SectionHeader("Search"),
2964 SettingsPageItem::SettingItem(SettingItem {
2965 title: "Whole Word",
2966 description: "Search for whole words by default.",
2967 field: Box::new(SettingField {
2968 json_path: Some("search.whole_word"),
2969 pick: |settings_content| {
2970 settings_content.editor.search.as_ref()?.whole_word.as_ref()
2971 },
2972 write: |settings_content, value| {
2973 settings_content
2974 .editor
2975 .search
2976 .get_or_insert_default()
2977 .whole_word = value;
2978 },
2979 }),
2980 metadata: None,
2981 files: USER,
2982 }),
2983 SettingsPageItem::SettingItem(SettingItem {
2984 title: "Case Sensitive",
2985 description: "Search case-sensitively by default.",
2986 field: Box::new(SettingField {
2987 json_path: Some("search.case_sensitive"),
2988 pick: |settings_content| {
2989 settings_content
2990 .editor
2991 .search
2992 .as_ref()?
2993 .case_sensitive
2994 .as_ref()
2995 },
2996 write: |settings_content, value| {
2997 settings_content
2998 .editor
2999 .search
3000 .get_or_insert_default()
3001 .case_sensitive = value;
3002 },
3003 }),
3004 metadata: None,
3005 files: USER,
3006 }),
3007 SettingsPageItem::SettingItem(SettingItem {
3008 title: "Use Smartcase Search",
3009 description: "Whether to automatically enable case-sensitive search based on the search query.",
3010 field: Box::new(SettingField {
3011 json_path: Some("use_smartcase_search"),
3012 pick: |settings_content| settings_content.editor.use_smartcase_search.as_ref(),
3013 write: |settings_content, value| {
3014 settings_content.editor.use_smartcase_search = value;
3015 },
3016 }),
3017 metadata: None,
3018 files: USER,
3019 }),
3020 SettingsPageItem::SettingItem(SettingItem {
3021 title: "Include Ignored",
3022 description: "Include ignored files in search results by default.",
3023 field: Box::new(SettingField {
3024 json_path: Some("search.include_ignored"),
3025 pick: |settings_content| {
3026 settings_content
3027 .editor
3028 .search
3029 .as_ref()?
3030 .include_ignored
3031 .as_ref()
3032 },
3033 write: |settings_content, value| {
3034 settings_content
3035 .editor
3036 .search
3037 .get_or_insert_default()
3038 .include_ignored = value;
3039 },
3040 }),
3041 metadata: None,
3042 files: USER,
3043 }),
3044 SettingsPageItem::SettingItem(SettingItem {
3045 title: "Regex",
3046 description: "Use regex search by default.",
3047 field: Box::new(SettingField {
3048 json_path: Some("search.regex"),
3049 pick: |settings_content| {
3050 settings_content.editor.search.as_ref()?.regex.as_ref()
3051 },
3052 write: |settings_content, value| {
3053 settings_content.editor.search.get_or_insert_default().regex = value;
3054 },
3055 }),
3056 metadata: None,
3057 files: USER,
3058 }),
3059 SettingsPageItem::SettingItem(SettingItem {
3060 title: "Search Wrap",
3061 description: "Whether the editor search results will loop.",
3062 field: Box::new(SettingField {
3063 json_path: Some("search_wrap"),
3064 pick: |settings_content| settings_content.editor.search_wrap.as_ref(),
3065 write: |settings_content, value| {
3066 settings_content.editor.search_wrap = value;
3067 },
3068 }),
3069 metadata: None,
3070 files: USER,
3071 }),
3072 SettingsPageItem::SettingItem(SettingItem {
3073 title: "Center on Match",
3074 description: "Whether to center the current match in the editor",
3075 field: Box::new(SettingField {
3076 json_path: Some("editor.search.center_on_match"),
3077 pick: |settings_content| {
3078 settings_content
3079 .editor
3080 .search
3081 .as_ref()
3082 .and_then(|search| search.center_on_match.as_ref())
3083 },
3084 write: |settings_content, value| {
3085 settings_content
3086 .editor
3087 .search
3088 .get_or_insert_default()
3089 .center_on_match = value;
3090 },
3091 }),
3092 metadata: None,
3093 files: USER,
3094 }),
3095 SettingsPageItem::SettingItem(SettingItem {
3096 title: "Seed Search Query From Cursor",
3097 description: "When to populate a new search's query based on the text under the cursor.",
3098 field: Box::new(SettingField {
3099 json_path: Some("seed_search_query_from_cursor"),
3100 pick: |settings_content| {
3101 settings_content
3102 .editor
3103 .seed_search_query_from_cursor
3104 .as_ref()
3105 },
3106 write: |settings_content, value| {
3107 settings_content.editor.seed_search_query_from_cursor = value;
3108 },
3109 }),
3110 metadata: None,
3111 files: USER,
3112 }),
3113 ]
3114 }
3115
3116 fn file_finder_section() -> [SettingsPageItem; 6] {
3117 [
3118 SettingsPageItem::SectionHeader("File Finder"),
3119 // todo: null by default
3120 SettingsPageItem::SettingItem(SettingItem {
3121 title: "Include Ignored in Search",
3122 description: "Use gitignored files when searching.",
3123 field: Box::new(SettingField {
3124 json_path: Some("file_finder.include_ignored"),
3125 pick: |settings_content| {
3126 settings_content
3127 .file_finder
3128 .as_ref()?
3129 .include_ignored
3130 .as_ref()
3131 },
3132 write: |settings_content, value| {
3133 settings_content
3134 .file_finder
3135 .get_or_insert_default()
3136 .include_ignored = value;
3137 },
3138 }),
3139 metadata: None,
3140 files: USER,
3141 }),
3142 SettingsPageItem::SettingItem(SettingItem {
3143 title: "File Icons",
3144 description: "Show file icons in the file finder.",
3145 field: Box::new(SettingField {
3146 json_path: Some("file_finder.file_icons"),
3147 pick: |settings_content| {
3148 settings_content.file_finder.as_ref()?.file_icons.as_ref()
3149 },
3150 write: |settings_content, value| {
3151 settings_content
3152 .file_finder
3153 .get_or_insert_default()
3154 .file_icons = value;
3155 },
3156 }),
3157 metadata: None,
3158 files: USER,
3159 }),
3160 SettingsPageItem::SettingItem(SettingItem {
3161 title: "Modal Max Width",
3162 description: "Determines how much space the file finder can take up in relation to the available window width.",
3163 field: Box::new(SettingField {
3164 json_path: Some("file_finder.modal_max_width"),
3165 pick: |settings_content| {
3166 settings_content
3167 .file_finder
3168 .as_ref()?
3169 .modal_max_width
3170 .as_ref()
3171 },
3172 write: |settings_content, value| {
3173 settings_content
3174 .file_finder
3175 .get_or_insert_default()
3176 .modal_max_width = value;
3177 },
3178 }),
3179 metadata: None,
3180 files: USER,
3181 }),
3182 SettingsPageItem::SettingItem(SettingItem {
3183 title: "Skip Focus For Active In Search",
3184 description: "Whether the file finder should skip focus for the active file in search results.",
3185 field: Box::new(SettingField {
3186 json_path: Some("file_finder.skip_focus_for_active_in_search"),
3187 pick: |settings_content| {
3188 settings_content
3189 .file_finder
3190 .as_ref()?
3191 .skip_focus_for_active_in_search
3192 .as_ref()
3193 },
3194 write: |settings_content, value| {
3195 settings_content
3196 .file_finder
3197 .get_or_insert_default()
3198 .skip_focus_for_active_in_search = value;
3199 },
3200 }),
3201 metadata: None,
3202 files: USER,
3203 }),
3204 SettingsPageItem::SettingItem(SettingItem {
3205 title: "Git Status",
3206 description: "Show the Git status in the file finder.",
3207 field: Box::new(SettingField {
3208 json_path: Some("file_finder.git_status"),
3209 pick: |settings_content| {
3210 settings_content.file_finder.as_ref()?.git_status.as_ref()
3211 },
3212 write: |settings_content, value| {
3213 settings_content
3214 .file_finder
3215 .get_or_insert_default()
3216 .git_status = value;
3217 },
3218 }),
3219 metadata: None,
3220 files: USER,
3221 }),
3222 ]
3223 }
3224
3225 fn file_scan_section() -> [SettingsPageItem; 5] {
3226 [
3227 SettingsPageItem::SectionHeader("File Scan"),
3228 SettingsPageItem::SettingItem(SettingItem {
3229 title: "File Scan Exclusions",
3230 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\"",
3231 field: Box::new(
3232 SettingField {
3233 json_path: Some("file_scan_exclusions"),
3234 pick: |settings_content| {
3235 settings_content
3236 .project
3237 .worktree
3238 .file_scan_exclusions
3239 .as_ref()
3240 },
3241 write: |settings_content, value| {
3242 settings_content.project.worktree.file_scan_exclusions = value;
3243 },
3244 }
3245 .unimplemented(),
3246 ),
3247 metadata: None,
3248 files: USER,
3249 }),
3250 SettingsPageItem::SettingItem(SettingItem {
3251 title: "File Scan Inclusions",
3252 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",
3253 field: Box::new(
3254 SettingField {
3255 json_path: Some("file_scan_inclusions"),
3256 pick: |settings_content| {
3257 settings_content
3258 .project
3259 .worktree
3260 .file_scan_inclusions
3261 .as_ref()
3262 },
3263 write: |settings_content, value| {
3264 settings_content.project.worktree.file_scan_inclusions = value;
3265 },
3266 }
3267 .unimplemented(),
3268 ),
3269 metadata: None,
3270 files: USER,
3271 }),
3272 SettingsPageItem::SettingItem(SettingItem {
3273 title: "Restore File State",
3274 description: "Restore previous file state when reopening.",
3275 field: Box::new(SettingField {
3276 json_path: Some("restore_on_file_reopen"),
3277 pick: |settings_content| {
3278 settings_content.workspace.restore_on_file_reopen.as_ref()
3279 },
3280 write: |settings_content, value| {
3281 settings_content.workspace.restore_on_file_reopen = value;
3282 },
3283 }),
3284 metadata: None,
3285 files: USER,
3286 }),
3287 SettingsPageItem::SettingItem(SettingItem {
3288 title: "Close on File Delete",
3289 description: "Automatically close files that have been deleted.",
3290 field: Box::new(SettingField {
3291 json_path: Some("close_on_file_delete"),
3292 pick: |settings_content| {
3293 settings_content.workspace.close_on_file_delete.as_ref()
3294 },
3295 write: |settings_content, value| {
3296 settings_content.workspace.close_on_file_delete = value;
3297 },
3298 }),
3299 metadata: None,
3300 files: USER,
3301 }),
3302 ]
3303 }
3304
3305 SettingsPage {
3306 title: "Search & Files",
3307 items: concat_sections![search_section(), file_finder_section(), file_scan_section()],
3308 }
3309}
3310
3311fn window_and_layout_page() -> SettingsPage {
3312 fn status_bar_section() -> [SettingsPageItem; 9] {
3313 [
3314 SettingsPageItem::SectionHeader("Status Bar"),
3315 SettingsPageItem::SettingItem(SettingItem {
3316 title: "Project Panel Button",
3317 description: "Show the project panel button in the status bar.",
3318 field: Box::new(SettingField {
3319 json_path: Some("project_panel.button"),
3320 pick: |settings_content| {
3321 settings_content.project_panel.as_ref()?.button.as_ref()
3322 },
3323 write: |settings_content, value| {
3324 settings_content
3325 .project_panel
3326 .get_or_insert_default()
3327 .button = value;
3328 },
3329 }),
3330 metadata: None,
3331 files: USER,
3332 }),
3333 SettingsPageItem::SettingItem(SettingItem {
3334 title: "Active Language Button",
3335 description: "Show the active language button in the status bar.",
3336 field: Box::new(SettingField {
3337 json_path: Some("status_bar.active_language_button"),
3338 pick: |settings_content| {
3339 settings_content
3340 .status_bar
3341 .as_ref()?
3342 .active_language_button
3343 .as_ref()
3344 },
3345 write: |settings_content, value| {
3346 settings_content
3347 .status_bar
3348 .get_or_insert_default()
3349 .active_language_button = value;
3350 },
3351 }),
3352 metadata: None,
3353 files: USER,
3354 }),
3355 SettingsPageItem::SettingItem(SettingItem {
3356 title: "Active Encoding Button",
3357 description: "Control when to show the active encoding in the status bar.",
3358 field: Box::new(SettingField {
3359 json_path: Some("status_bar.active_encoding_button"),
3360 pick: |settings_content| {
3361 settings_content
3362 .status_bar
3363 .as_ref()?
3364 .active_encoding_button
3365 .as_ref()
3366 },
3367 write: |settings_content, value| {
3368 settings_content
3369 .status_bar
3370 .get_or_insert_default()
3371 .active_encoding_button = value;
3372 },
3373 }),
3374 metadata: None,
3375 files: USER,
3376 }),
3377 SettingsPageItem::SettingItem(SettingItem {
3378 title: "Cursor Position Button",
3379 description: "Show the cursor position button in the status bar.",
3380 field: Box::new(SettingField {
3381 json_path: Some("status_bar.cursor_position_button"),
3382 pick: |settings_content| {
3383 settings_content
3384 .status_bar
3385 .as_ref()?
3386 .cursor_position_button
3387 .as_ref()
3388 },
3389 write: |settings_content, value| {
3390 settings_content
3391 .status_bar
3392 .get_or_insert_default()
3393 .cursor_position_button = value;
3394 },
3395 }),
3396 metadata: None,
3397 files: USER,
3398 }),
3399 SettingsPageItem::SettingItem(SettingItem {
3400 title: "Terminal Button",
3401 description: "Show the terminal button in the status bar.",
3402 field: Box::new(SettingField {
3403 json_path: Some("terminal.button"),
3404 pick: |settings_content| settings_content.terminal.as_ref()?.button.as_ref(),
3405 write: |settings_content, value| {
3406 settings_content.terminal.get_or_insert_default().button = value;
3407 },
3408 }),
3409 metadata: None,
3410 files: USER,
3411 }),
3412 SettingsPageItem::SettingItem(SettingItem {
3413 title: "Diagnostics Button",
3414 description: "Show the project diagnostics button in the status bar.",
3415 field: Box::new(SettingField {
3416 json_path: Some("diagnostics.button"),
3417 pick: |settings_content| settings_content.diagnostics.as_ref()?.button.as_ref(),
3418 write: |settings_content, value| {
3419 settings_content.diagnostics.get_or_insert_default().button = value;
3420 },
3421 }),
3422 metadata: None,
3423 files: USER,
3424 }),
3425 SettingsPageItem::SettingItem(SettingItem {
3426 title: "Project Search Button",
3427 description: "Show the project search button in the status bar.",
3428 field: Box::new(SettingField {
3429 json_path: Some("search.button"),
3430 pick: |settings_content| {
3431 settings_content.editor.search.as_ref()?.button.as_ref()
3432 },
3433 write: |settings_content, value| {
3434 settings_content
3435 .editor
3436 .search
3437 .get_or_insert_default()
3438 .button = value;
3439 },
3440 }),
3441 metadata: None,
3442 files: USER,
3443 }),
3444 SettingsPageItem::SettingItem(SettingItem {
3445 title: "Debugger Button",
3446 description: "Show the debugger button in the status bar.",
3447 field: Box::new(SettingField {
3448 json_path: Some("debugger.button"),
3449 pick: |settings_content| settings_content.debugger.as_ref()?.button.as_ref(),
3450 write: |settings_content, value| {
3451 settings_content.debugger.get_or_insert_default().button = value;
3452 },
3453 }),
3454 metadata: None,
3455 files: USER,
3456 }),
3457 ]
3458 }
3459
3460 fn title_bar_section() -> [SettingsPageItem; 9] {
3461 [
3462 SettingsPageItem::SectionHeader("Title Bar"),
3463 SettingsPageItem::SettingItem(SettingItem {
3464 title: "Show Branch Icon",
3465 description: "Show the branch icon beside branch switcher in the titlebar.",
3466 field: Box::new(SettingField {
3467 json_path: Some("title_bar.show_branch_icon"),
3468 pick: |settings_content| {
3469 settings_content
3470 .title_bar
3471 .as_ref()?
3472 .show_branch_icon
3473 .as_ref()
3474 },
3475 write: |settings_content, value| {
3476 settings_content
3477 .title_bar
3478 .get_or_insert_default()
3479 .show_branch_icon = value;
3480 },
3481 }),
3482 metadata: None,
3483 files: USER,
3484 }),
3485 SettingsPageItem::SettingItem(SettingItem {
3486 title: "Show Branch Name",
3487 description: "Show the branch name button in the titlebar.",
3488 field: Box::new(SettingField {
3489 json_path: Some("title_bar.show_branch_name"),
3490 pick: |settings_content| {
3491 settings_content
3492 .title_bar
3493 .as_ref()?
3494 .show_branch_name
3495 .as_ref()
3496 },
3497 write: |settings_content, value| {
3498 settings_content
3499 .title_bar
3500 .get_or_insert_default()
3501 .show_branch_name = value;
3502 },
3503 }),
3504 metadata: None,
3505 files: USER,
3506 }),
3507 SettingsPageItem::SettingItem(SettingItem {
3508 title: "Show Project Items",
3509 description: "Show the project host and name in the titlebar.",
3510 field: Box::new(SettingField {
3511 json_path: Some("title_bar.show_project_items"),
3512 pick: |settings_content| {
3513 settings_content
3514 .title_bar
3515 .as_ref()?
3516 .show_project_items
3517 .as_ref()
3518 },
3519 write: |settings_content, value| {
3520 settings_content
3521 .title_bar
3522 .get_or_insert_default()
3523 .show_project_items = value;
3524 },
3525 }),
3526 metadata: None,
3527 files: USER,
3528 }),
3529 SettingsPageItem::SettingItem(SettingItem {
3530 title: "Show Onboarding Banner",
3531 description: "Show banners announcing new features in the titlebar.",
3532 field: Box::new(SettingField {
3533 json_path: Some("title_bar.show_onboarding_banner"),
3534 pick: |settings_content| {
3535 settings_content
3536 .title_bar
3537 .as_ref()?
3538 .show_onboarding_banner
3539 .as_ref()
3540 },
3541 write: |settings_content, value| {
3542 settings_content
3543 .title_bar
3544 .get_or_insert_default()
3545 .show_onboarding_banner = value;
3546 },
3547 }),
3548 metadata: None,
3549 files: USER,
3550 }),
3551 SettingsPageItem::SettingItem(SettingItem {
3552 title: "Show Sign In",
3553 description: "Show the sign in button in the titlebar.",
3554 field: Box::new(SettingField {
3555 json_path: Some("title_bar.show_sign_in"),
3556 pick: |settings_content| {
3557 settings_content.title_bar.as_ref()?.show_sign_in.as_ref()
3558 },
3559 write: |settings_content, value| {
3560 settings_content
3561 .title_bar
3562 .get_or_insert_default()
3563 .show_sign_in = value;
3564 },
3565 }),
3566 metadata: None,
3567 files: USER,
3568 }),
3569 SettingsPageItem::SettingItem(SettingItem {
3570 title: "Show User Menu",
3571 description: "Show the user menu button in the titlebar.",
3572 field: Box::new(SettingField {
3573 json_path: Some("title_bar.show_user_menu"),
3574 pick: |settings_content| {
3575 settings_content.title_bar.as_ref()?.show_user_menu.as_ref()
3576 },
3577 write: |settings_content, value| {
3578 settings_content
3579 .title_bar
3580 .get_or_insert_default()
3581 .show_user_menu = value;
3582 },
3583 }),
3584 metadata: None,
3585 files: USER,
3586 }),
3587 SettingsPageItem::SettingItem(SettingItem {
3588 title: "Show User Picture",
3589 description: "Show user picture in the titlebar.",
3590 field: Box::new(SettingField {
3591 json_path: Some("title_bar.show_user_picture"),
3592 pick: |settings_content| {
3593 settings_content
3594 .title_bar
3595 .as_ref()?
3596 .show_user_picture
3597 .as_ref()
3598 },
3599 write: |settings_content, value| {
3600 settings_content
3601 .title_bar
3602 .get_or_insert_default()
3603 .show_user_picture = value;
3604 },
3605 }),
3606 metadata: None,
3607 files: USER,
3608 }),
3609 SettingsPageItem::SettingItem(SettingItem {
3610 title: "Show Menus",
3611 description: "Show the menus in the titlebar.",
3612 field: Box::new(SettingField {
3613 json_path: Some("title_bar.show_menus"),
3614 pick: |settings_content| {
3615 settings_content.title_bar.as_ref()?.show_menus.as_ref()
3616 },
3617 write: |settings_content, value| {
3618 settings_content
3619 .title_bar
3620 .get_or_insert_default()
3621 .show_menus = value;
3622 },
3623 }),
3624 metadata: None,
3625 files: USER,
3626 }),
3627 ]
3628 }
3629
3630 fn tab_bar_section() -> [SettingsPageItem; 9] {
3631 [
3632 SettingsPageItem::SectionHeader("Tab Bar"),
3633 SettingsPageItem::SettingItem(SettingItem {
3634 title: "Show Tab Bar",
3635 description: "Show the tab bar in the editor.",
3636 field: Box::new(SettingField {
3637 json_path: Some("tab_bar.show"),
3638 pick: |settings_content| settings_content.tab_bar.as_ref()?.show.as_ref(),
3639 write: |settings_content, value| {
3640 settings_content.tab_bar.get_or_insert_default().show = value;
3641 },
3642 }),
3643 metadata: None,
3644 files: USER,
3645 }),
3646 SettingsPageItem::SettingItem(SettingItem {
3647 title: "Show Git Status In Tabs",
3648 description: "Show the Git file status on a tab item.",
3649 field: Box::new(SettingField {
3650 json_path: Some("tabs.git_status"),
3651 pick: |settings_content| settings_content.tabs.as_ref()?.git_status.as_ref(),
3652 write: |settings_content, value| {
3653 settings_content.tabs.get_or_insert_default().git_status = value;
3654 },
3655 }),
3656 metadata: None,
3657 files: USER,
3658 }),
3659 SettingsPageItem::SettingItem(SettingItem {
3660 title: "Show File Icons In Tabs",
3661 description: "Show the file icon for a tab.",
3662 field: Box::new(SettingField {
3663 json_path: Some("tabs.file_icons"),
3664 pick: |settings_content| settings_content.tabs.as_ref()?.file_icons.as_ref(),
3665 write: |settings_content, value| {
3666 settings_content.tabs.get_or_insert_default().file_icons = value;
3667 },
3668 }),
3669 metadata: None,
3670 files: USER,
3671 }),
3672 SettingsPageItem::SettingItem(SettingItem {
3673 title: "Tab Close Position",
3674 description: "Position of the close button in a tab.",
3675 field: Box::new(SettingField {
3676 json_path: Some("tabs.close_position"),
3677 pick: |settings_content| {
3678 settings_content.tabs.as_ref()?.close_position.as_ref()
3679 },
3680 write: |settings_content, value| {
3681 settings_content.tabs.get_or_insert_default().close_position = value;
3682 },
3683 }),
3684 metadata: None,
3685 files: USER,
3686 }),
3687 SettingsPageItem::SettingItem(SettingItem {
3688 files: USER,
3689 title: "Maximum Tabs",
3690 description: "Maximum open tabs in a pane. Will not close an unsaved tab.",
3691 // todo(settings_ui): The default for this value is null and it's use in code
3692 // is complex, so I'm going to come back to this later
3693 field: Box::new(
3694 SettingField {
3695 json_path: Some("max_tabs"),
3696 pick: |settings_content| settings_content.workspace.max_tabs.as_ref(),
3697 write: |settings_content, value| {
3698 settings_content.workspace.max_tabs = value;
3699 },
3700 }
3701 .unimplemented(),
3702 ),
3703 metadata: None,
3704 }),
3705 SettingsPageItem::SettingItem(SettingItem {
3706 title: "Show Navigation History Buttons",
3707 description: "Show the navigation history buttons in the tab bar.",
3708 field: Box::new(SettingField {
3709 json_path: Some("tab_bar.show_nav_history_buttons"),
3710 pick: |settings_content| {
3711 settings_content
3712 .tab_bar
3713 .as_ref()?
3714 .show_nav_history_buttons
3715 .as_ref()
3716 },
3717 write: |settings_content, value| {
3718 settings_content
3719 .tab_bar
3720 .get_or_insert_default()
3721 .show_nav_history_buttons = value;
3722 },
3723 }),
3724 metadata: None,
3725 files: USER,
3726 }),
3727 SettingsPageItem::SettingItem(SettingItem {
3728 title: "Show Tab Bar Buttons",
3729 description: "Show the tab bar buttons (New, Split Pane, Zoom).",
3730 field: Box::new(SettingField {
3731 json_path: Some("tab_bar.show_tab_bar_buttons"),
3732 pick: |settings_content| {
3733 settings_content
3734 .tab_bar
3735 .as_ref()?
3736 .show_tab_bar_buttons
3737 .as_ref()
3738 },
3739 write: |settings_content, value| {
3740 settings_content
3741 .tab_bar
3742 .get_or_insert_default()
3743 .show_tab_bar_buttons = value;
3744 },
3745 }),
3746 metadata: None,
3747 files: USER,
3748 }),
3749 SettingsPageItem::SettingItem(SettingItem {
3750 title: "Pinned Tabs Layout",
3751 description: "Show pinned tabs in a separate row above unpinned tabs.",
3752 field: Box::new(SettingField {
3753 json_path: Some("tab_bar.show_pinned_tabs_in_separate_row"),
3754 pick: |settings_content| {
3755 settings_content
3756 .tab_bar
3757 .as_ref()?
3758 .show_pinned_tabs_in_separate_row
3759 .as_ref()
3760 },
3761 write: |settings_content, value| {
3762 settings_content
3763 .tab_bar
3764 .get_or_insert_default()
3765 .show_pinned_tabs_in_separate_row = value;
3766 },
3767 }),
3768 metadata: None,
3769 files: USER,
3770 }),
3771 ]
3772 }
3773
3774 fn tab_settings_section() -> [SettingsPageItem; 4] {
3775 [
3776 SettingsPageItem::SectionHeader("Tab Settings"),
3777 SettingsPageItem::SettingItem(SettingItem {
3778 title: "Activate On Close",
3779 description: "What to do after closing the current tab.",
3780 field: Box::new(SettingField {
3781 json_path: Some("tabs.activate_on_close"),
3782 pick: |settings_content| {
3783 settings_content.tabs.as_ref()?.activate_on_close.as_ref()
3784 },
3785 write: |settings_content, value| {
3786 settings_content
3787 .tabs
3788 .get_or_insert_default()
3789 .activate_on_close = value;
3790 },
3791 }),
3792 metadata: None,
3793 files: USER,
3794 }),
3795 SettingsPageItem::SettingItem(SettingItem {
3796 title: "Tab Show Diagnostics",
3797 description: "Which files containing diagnostic errors/warnings to mark in the tabs.",
3798 field: Box::new(SettingField {
3799 json_path: Some("tabs.show_diagnostics"),
3800 pick: |settings_content| {
3801 settings_content.tabs.as_ref()?.show_diagnostics.as_ref()
3802 },
3803 write: |settings_content, value| {
3804 settings_content
3805 .tabs
3806 .get_or_insert_default()
3807 .show_diagnostics = value;
3808 },
3809 }),
3810 metadata: None,
3811 files: USER,
3812 }),
3813 SettingsPageItem::SettingItem(SettingItem {
3814 title: "Show Close Button",
3815 description: "Controls the appearance behavior of the tab's close button.",
3816 field: Box::new(SettingField {
3817 json_path: Some("tabs.show_close_button"),
3818 pick: |settings_content| {
3819 settings_content.tabs.as_ref()?.show_close_button.as_ref()
3820 },
3821 write: |settings_content, value| {
3822 settings_content
3823 .tabs
3824 .get_or_insert_default()
3825 .show_close_button = value;
3826 },
3827 }),
3828 metadata: None,
3829 files: USER,
3830 }),
3831 ]
3832 }
3833
3834 fn preview_tabs_section() -> [SettingsPageItem; 8] {
3835 [
3836 SettingsPageItem::SectionHeader("Preview Tabs"),
3837 SettingsPageItem::SettingItem(SettingItem {
3838 title: "Preview Tabs Enabled",
3839 description: "Show opened editors as preview tabs.",
3840 field: Box::new(SettingField {
3841 json_path: Some("preview_tabs.enabled"),
3842 pick: |settings_content| {
3843 settings_content.preview_tabs.as_ref()?.enabled.as_ref()
3844 },
3845 write: |settings_content, value| {
3846 settings_content
3847 .preview_tabs
3848 .get_or_insert_default()
3849 .enabled = value;
3850 },
3851 }),
3852 metadata: None,
3853 files: USER,
3854 }),
3855 SettingsPageItem::SettingItem(SettingItem {
3856 title: "Enable Preview From Project Panel",
3857 description: "Whether to open tabs in preview mode when opened from the project panel with a single click.",
3858 field: Box::new(SettingField {
3859 json_path: Some("preview_tabs.enable_preview_from_project_panel"),
3860 pick: |settings_content| {
3861 settings_content
3862 .preview_tabs
3863 .as_ref()?
3864 .enable_preview_from_project_panel
3865 .as_ref()
3866 },
3867 write: |settings_content, value| {
3868 settings_content
3869 .preview_tabs
3870 .get_or_insert_default()
3871 .enable_preview_from_project_panel = value;
3872 },
3873 }),
3874 metadata: None,
3875 files: USER,
3876 }),
3877 SettingsPageItem::SettingItem(SettingItem {
3878 title: "Enable Preview From File Finder",
3879 description: "Whether to open tabs in preview mode when selected from the file finder.",
3880 field: Box::new(SettingField {
3881 json_path: Some("preview_tabs.enable_preview_from_file_finder"),
3882 pick: |settings_content| {
3883 settings_content
3884 .preview_tabs
3885 .as_ref()?
3886 .enable_preview_from_file_finder
3887 .as_ref()
3888 },
3889 write: |settings_content, value| {
3890 settings_content
3891 .preview_tabs
3892 .get_or_insert_default()
3893 .enable_preview_from_file_finder = value;
3894 },
3895 }),
3896 metadata: None,
3897 files: USER,
3898 }),
3899 SettingsPageItem::SettingItem(SettingItem {
3900 title: "Enable Preview From Multibuffer",
3901 description: "Whether to open tabs in preview mode when opened from a multibuffer.",
3902 field: Box::new(SettingField {
3903 json_path: Some("preview_tabs.enable_preview_from_multibuffer"),
3904 pick: |settings_content| {
3905 settings_content
3906 .preview_tabs
3907 .as_ref()?
3908 .enable_preview_from_multibuffer
3909 .as_ref()
3910 },
3911 write: |settings_content, value| {
3912 settings_content
3913 .preview_tabs
3914 .get_or_insert_default()
3915 .enable_preview_from_multibuffer = value;
3916 },
3917 }),
3918 metadata: None,
3919 files: USER,
3920 }),
3921 SettingsPageItem::SettingItem(SettingItem {
3922 title: "Enable Preview Multibuffer From Code Navigation",
3923 description: "Whether to open tabs in preview mode when code navigation is used to open a multibuffer.",
3924 field: Box::new(SettingField {
3925 json_path: Some("preview_tabs.enable_preview_multibuffer_from_code_navigation"),
3926 pick: |settings_content| {
3927 settings_content
3928 .preview_tabs
3929 .as_ref()?
3930 .enable_preview_multibuffer_from_code_navigation
3931 .as_ref()
3932 },
3933 write: |settings_content, value| {
3934 settings_content
3935 .preview_tabs
3936 .get_or_insert_default()
3937 .enable_preview_multibuffer_from_code_navigation = value;
3938 },
3939 }),
3940 metadata: None,
3941 files: USER,
3942 }),
3943 SettingsPageItem::SettingItem(SettingItem {
3944 title: "Enable Preview File From Code Navigation",
3945 description: "Whether to open tabs in preview mode when code navigation is used to open a single file.",
3946 field: Box::new(SettingField {
3947 json_path: Some("preview_tabs.enable_preview_file_from_code_navigation"),
3948 pick: |settings_content| {
3949 settings_content
3950 .preview_tabs
3951 .as_ref()?
3952 .enable_preview_file_from_code_navigation
3953 .as_ref()
3954 },
3955 write: |settings_content, value| {
3956 settings_content
3957 .preview_tabs
3958 .get_or_insert_default()
3959 .enable_preview_file_from_code_navigation = value;
3960 },
3961 }),
3962 metadata: None,
3963 files: USER,
3964 }),
3965 SettingsPageItem::SettingItem(SettingItem {
3966 title: "Enable Keep Preview On Code Navigation",
3967 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.",
3968 field: Box::new(SettingField {
3969 json_path: Some("preview_tabs.enable_keep_preview_on_code_navigation"),
3970 pick: |settings_content| {
3971 settings_content
3972 .preview_tabs
3973 .as_ref()?
3974 .enable_keep_preview_on_code_navigation
3975 .as_ref()
3976 },
3977 write: |settings_content, value| {
3978 settings_content
3979 .preview_tabs
3980 .get_or_insert_default()
3981 .enable_keep_preview_on_code_navigation = value;
3982 },
3983 }),
3984 metadata: None,
3985 files: USER,
3986 }),
3987 ]
3988 }
3989
3990 fn layout_section() -> [SettingsPageItem; 4] {
3991 [
3992 SettingsPageItem::SectionHeader("Layout"),
3993 SettingsPageItem::SettingItem(SettingItem {
3994 title: "Bottom Dock Layout",
3995 description: "Layout mode for the bottom dock.",
3996 field: Box::new(SettingField {
3997 json_path: Some("bottom_dock_layout"),
3998 pick: |settings_content| settings_content.workspace.bottom_dock_layout.as_ref(),
3999 write: |settings_content, value| {
4000 settings_content.workspace.bottom_dock_layout = value;
4001 },
4002 }),
4003 metadata: None,
4004 files: USER,
4005 }),
4006 SettingsPageItem::SettingItem(SettingItem {
4007 files: USER,
4008 title: "Centered Layout Left Padding",
4009 description: "Left padding for centered layout.",
4010 field: Box::new(SettingField {
4011 json_path: Some("centered_layout.left_padding"),
4012 pick: |settings_content| {
4013 settings_content
4014 .workspace
4015 .centered_layout
4016 .as_ref()?
4017 .left_padding
4018 .as_ref()
4019 },
4020 write: |settings_content, value| {
4021 settings_content
4022 .workspace
4023 .centered_layout
4024 .get_or_insert_default()
4025 .left_padding = value;
4026 },
4027 }),
4028 metadata: None,
4029 }),
4030 SettingsPageItem::SettingItem(SettingItem {
4031 files: USER,
4032 title: "Centered Layout Right Padding",
4033 description: "Right padding for centered layout.",
4034 field: Box::new(SettingField {
4035 json_path: Some("centered_layout.right_padding"),
4036 pick: |settings_content| {
4037 settings_content
4038 .workspace
4039 .centered_layout
4040 .as_ref()?
4041 .right_padding
4042 .as_ref()
4043 },
4044 write: |settings_content, value| {
4045 settings_content
4046 .workspace
4047 .centered_layout
4048 .get_or_insert_default()
4049 .right_padding = value;
4050 },
4051 }),
4052 metadata: None,
4053 }),
4054 ]
4055 }
4056
4057 fn window_section() -> [SettingsPageItem; 3] {
4058 [
4059 SettingsPageItem::SectionHeader("Window"),
4060 // todo(settings_ui): Should we filter by platform.as_ref()?
4061 SettingsPageItem::SettingItem(SettingItem {
4062 title: "Use System Window Tabs",
4063 description: "(macOS only) whether to allow Windows to tab together.",
4064 field: Box::new(SettingField {
4065 json_path: Some("use_system_window_tabs"),
4066 pick: |settings_content| {
4067 settings_content.workspace.use_system_window_tabs.as_ref()
4068 },
4069 write: |settings_content, value| {
4070 settings_content.workspace.use_system_window_tabs = value;
4071 },
4072 }),
4073 metadata: None,
4074 files: USER,
4075 }),
4076 SettingsPageItem::SettingItem(SettingItem {
4077 title: "Window Decorations",
4078 description: "(Linux only) whether Zed or your compositor should draw window decorations.",
4079 field: Box::new(SettingField {
4080 json_path: Some("window_decorations"),
4081 pick: |settings_content| settings_content.workspace.window_decorations.as_ref(),
4082 write: |settings_content, value| {
4083 settings_content.workspace.window_decorations = value;
4084 },
4085 }),
4086 metadata: None,
4087 files: USER,
4088 }),
4089 ]
4090 }
4091
4092 fn pane_modifiers_section() -> [SettingsPageItem; 4] {
4093 [
4094 SettingsPageItem::SectionHeader("Pane Modifiers"),
4095 SettingsPageItem::SettingItem(SettingItem {
4096 title: "Inactive Opacity",
4097 description: "Opacity of inactive panels (0.0 - 1.0).",
4098 field: Box::new(SettingField {
4099 json_path: Some("active_pane_modifiers.inactive_opacity"),
4100 pick: |settings_content| {
4101 settings_content
4102 .workspace
4103 .active_pane_modifiers
4104 .as_ref()?
4105 .inactive_opacity
4106 .as_ref()
4107 },
4108 write: |settings_content, value| {
4109 settings_content
4110 .workspace
4111 .active_pane_modifiers
4112 .get_or_insert_default()
4113 .inactive_opacity = value;
4114 },
4115 }),
4116 metadata: None,
4117 files: USER,
4118 }),
4119 SettingsPageItem::SettingItem(SettingItem {
4120 title: "Border Size",
4121 description: "Size of the border surrounding the active pane.",
4122 field: Box::new(SettingField {
4123 json_path: Some("active_pane_modifiers.border_size"),
4124 pick: |settings_content| {
4125 settings_content
4126 .workspace
4127 .active_pane_modifiers
4128 .as_ref()?
4129 .border_size
4130 .as_ref()
4131 },
4132 write: |settings_content, value| {
4133 settings_content
4134 .workspace
4135 .active_pane_modifiers
4136 .get_or_insert_default()
4137 .border_size = value;
4138 },
4139 }),
4140 metadata: None,
4141 files: USER,
4142 }),
4143 SettingsPageItem::SettingItem(SettingItem {
4144 title: "Zoomed Padding",
4145 description: "Show padding for zoomed panes.",
4146 field: Box::new(SettingField {
4147 json_path: Some("zoomed_padding"),
4148 pick: |settings_content| settings_content.workspace.zoomed_padding.as_ref(),
4149 write: |settings_content, value| {
4150 settings_content.workspace.zoomed_padding = value;
4151 },
4152 }),
4153 metadata: None,
4154 files: USER,
4155 }),
4156 ]
4157 }
4158
4159 fn pane_split_direction_section() -> [SettingsPageItem; 3] {
4160 [
4161 SettingsPageItem::SectionHeader("Pane Split Direction"),
4162 SettingsPageItem::SettingItem(SettingItem {
4163 title: "Vertical Split Direction",
4164 description: "Direction to split vertically.",
4165 field: Box::new(SettingField {
4166 json_path: Some("pane_split_direction_vertical"),
4167 pick: |settings_content| {
4168 settings_content
4169 .workspace
4170 .pane_split_direction_vertical
4171 .as_ref()
4172 },
4173 write: |settings_content, value| {
4174 settings_content.workspace.pane_split_direction_vertical = value;
4175 },
4176 }),
4177 metadata: None,
4178 files: USER,
4179 }),
4180 SettingsPageItem::SettingItem(SettingItem {
4181 title: "Horizontal Split Direction",
4182 description: "Direction to split horizontally.",
4183 field: Box::new(SettingField {
4184 json_path: Some("pane_split_direction_horizontal"),
4185 pick: |settings_content| {
4186 settings_content
4187 .workspace
4188 .pane_split_direction_horizontal
4189 .as_ref()
4190 },
4191 write: |settings_content, value| {
4192 settings_content.workspace.pane_split_direction_horizontal = value;
4193 },
4194 }),
4195 metadata: None,
4196 files: USER,
4197 }),
4198 ]
4199 }
4200
4201 SettingsPage {
4202 title: "Window & Layout",
4203 items: concat_sections![
4204 status_bar_section(),
4205 title_bar_section(),
4206 tab_bar_section(),
4207 tab_settings_section(),
4208 preview_tabs_section(),
4209 layout_section(),
4210 window_section(),
4211 pane_modifiers_section(),
4212 pane_split_direction_section(),
4213 ],
4214 }
4215}
4216
4217fn panels_page() -> SettingsPage {
4218 fn project_panel_section() -> [SettingsPageItem; 20] {
4219 [
4220 SettingsPageItem::SectionHeader("Project Panel"),
4221 SettingsPageItem::SettingItem(SettingItem {
4222 title: "Project Panel Dock",
4223 description: "Where to dock the project panel.",
4224 field: Box::new(SettingField {
4225 json_path: Some("project_panel.dock"),
4226 pick: |settings_content| settings_content.project_panel.as_ref()?.dock.as_ref(),
4227 write: |settings_content, value| {
4228 settings_content.project_panel.get_or_insert_default().dock = value;
4229 },
4230 }),
4231 metadata: None,
4232 files: USER,
4233 }),
4234 SettingsPageItem::SettingItem(SettingItem {
4235 title: "Project Panel Default Width",
4236 description: "Default width of the project panel in pixels.",
4237 field: Box::new(SettingField {
4238 json_path: Some("project_panel.default_width"),
4239 pick: |settings_content| {
4240 settings_content
4241 .project_panel
4242 .as_ref()?
4243 .default_width
4244 .as_ref()
4245 },
4246 write: |settings_content, value| {
4247 settings_content
4248 .project_panel
4249 .get_or_insert_default()
4250 .default_width = value;
4251 },
4252 }),
4253 metadata: None,
4254 files: USER,
4255 }),
4256 SettingsPageItem::SettingItem(SettingItem {
4257 title: "Hide .gitignore",
4258 description: "Whether to hide the gitignore entries in the project panel.",
4259 field: Box::new(SettingField {
4260 json_path: Some("project_panel.hide_gitignore"),
4261 pick: |settings_content| {
4262 settings_content
4263 .project_panel
4264 .as_ref()?
4265 .hide_gitignore
4266 .as_ref()
4267 },
4268 write: |settings_content, value| {
4269 settings_content
4270 .project_panel
4271 .get_or_insert_default()
4272 .hide_gitignore = value;
4273 },
4274 }),
4275 metadata: None,
4276 files: USER,
4277 }),
4278 SettingsPageItem::SettingItem(SettingItem {
4279 title: "Entry Spacing",
4280 description: "Spacing between worktree entries in the project panel.",
4281 field: Box::new(SettingField {
4282 json_path: Some("project_panel.entry_spacing"),
4283 pick: |settings_content| {
4284 settings_content
4285 .project_panel
4286 .as_ref()?
4287 .entry_spacing
4288 .as_ref()
4289 },
4290 write: |settings_content, value| {
4291 settings_content
4292 .project_panel
4293 .get_or_insert_default()
4294 .entry_spacing = value;
4295 },
4296 }),
4297 metadata: None,
4298 files: USER,
4299 }),
4300 SettingsPageItem::SettingItem(SettingItem {
4301 title: "File Icons",
4302 description: "Show file icons in the project panel.",
4303 field: Box::new(SettingField {
4304 json_path: Some("project_panel.file_icons"),
4305 pick: |settings_content| {
4306 settings_content.project_panel.as_ref()?.file_icons.as_ref()
4307 },
4308 write: |settings_content, value| {
4309 settings_content
4310 .project_panel
4311 .get_or_insert_default()
4312 .file_icons = value;
4313 },
4314 }),
4315 metadata: None,
4316 files: USER,
4317 }),
4318 SettingsPageItem::SettingItem(SettingItem {
4319 title: "Folder Icons",
4320 description: "Whether to show folder icons or chevrons for directories in the project panel.",
4321 field: Box::new(SettingField {
4322 json_path: Some("project_panel.folder_icons"),
4323 pick: |settings_content| {
4324 settings_content
4325 .project_panel
4326 .as_ref()?
4327 .folder_icons
4328 .as_ref()
4329 },
4330 write: |settings_content, value| {
4331 settings_content
4332 .project_panel
4333 .get_or_insert_default()
4334 .folder_icons = value;
4335 },
4336 }),
4337 metadata: None,
4338 files: USER,
4339 }),
4340 SettingsPageItem::SettingItem(SettingItem {
4341 title: "Git Status",
4342 description: "Show the Git status in the project panel.",
4343 field: Box::new(SettingField {
4344 json_path: Some("project_panel.git_status"),
4345 pick: |settings_content| {
4346 settings_content.project_panel.as_ref()?.git_status.as_ref()
4347 },
4348 write: |settings_content, value| {
4349 settings_content
4350 .project_panel
4351 .get_or_insert_default()
4352 .git_status = value;
4353 },
4354 }),
4355 metadata: None,
4356 files: USER,
4357 }),
4358 SettingsPageItem::SettingItem(SettingItem {
4359 title: "Indent Size",
4360 description: "Amount of indentation for nested items.",
4361 field: Box::new(SettingField {
4362 json_path: Some("project_panel.indent_size"),
4363 pick: |settings_content| {
4364 settings_content
4365 .project_panel
4366 .as_ref()?
4367 .indent_size
4368 .as_ref()
4369 },
4370 write: |settings_content, value| {
4371 settings_content
4372 .project_panel
4373 .get_or_insert_default()
4374 .indent_size = value;
4375 },
4376 }),
4377 metadata: None,
4378 files: USER,
4379 }),
4380 SettingsPageItem::SettingItem(SettingItem {
4381 title: "Auto Reveal Entries",
4382 description: "Whether to reveal entries in the project panel automatically when a corresponding project entry becomes active.",
4383 field: Box::new(SettingField {
4384 json_path: Some("project_panel.auto_reveal_entries"),
4385 pick: |settings_content| {
4386 settings_content
4387 .project_panel
4388 .as_ref()?
4389 .auto_reveal_entries
4390 .as_ref()
4391 },
4392 write: |settings_content, value| {
4393 settings_content
4394 .project_panel
4395 .get_or_insert_default()
4396 .auto_reveal_entries = value;
4397 },
4398 }),
4399 metadata: None,
4400 files: USER,
4401 }),
4402 SettingsPageItem::SettingItem(SettingItem {
4403 title: "Starts Open",
4404 description: "Whether the project panel should open on startup.",
4405 field: Box::new(SettingField {
4406 json_path: Some("project_panel.starts_open"),
4407 pick: |settings_content| {
4408 settings_content
4409 .project_panel
4410 .as_ref()?
4411 .starts_open
4412 .as_ref()
4413 },
4414 write: |settings_content, value| {
4415 settings_content
4416 .project_panel
4417 .get_or_insert_default()
4418 .starts_open = value;
4419 },
4420 }),
4421 metadata: None,
4422 files: USER,
4423 }),
4424 SettingsPageItem::SettingItem(SettingItem {
4425 title: "Auto Fold Directories",
4426 description: "Whether to fold directories automatically and show compact folders when a directory has only one subdirectory inside.",
4427 field: Box::new(SettingField {
4428 json_path: Some("project_panel.auto_fold_dirs"),
4429 pick: |settings_content| {
4430 settings_content
4431 .project_panel
4432 .as_ref()?
4433 .auto_fold_dirs
4434 .as_ref()
4435 },
4436 write: |settings_content, value| {
4437 settings_content
4438 .project_panel
4439 .get_or_insert_default()
4440 .auto_fold_dirs = value;
4441 },
4442 }),
4443 metadata: None,
4444 files: USER,
4445 }),
4446 SettingsPageItem::SettingItem(SettingItem {
4447 title: "Show Scrollbar",
4448 description: "Show the scrollbar in the project panel.",
4449 field: Box::new(SettingField {
4450 json_path: Some("project_panel.scrollbar.show"),
4451 pick: |settings_content| {
4452 show_scrollbar_or_editor(settings_content, |settings_content| {
4453 settings_content
4454 .project_panel
4455 .as_ref()?
4456 .scrollbar
4457 .as_ref()?
4458 .show
4459 .as_ref()
4460 })
4461 },
4462 write: |settings_content, value| {
4463 settings_content
4464 .project_panel
4465 .get_or_insert_default()
4466 .scrollbar
4467 .get_or_insert_default()
4468 .show = value;
4469 },
4470 }),
4471 metadata: None,
4472 files: USER,
4473 }),
4474 SettingsPageItem::SettingItem(SettingItem {
4475 title: "Show Diagnostics",
4476 description: "Which files containing diagnostic errors/warnings to mark in the project panel.",
4477 field: Box::new(SettingField {
4478 json_path: Some("project_panel.show_diagnostics"),
4479 pick: |settings_content| {
4480 settings_content
4481 .project_panel
4482 .as_ref()?
4483 .show_diagnostics
4484 .as_ref()
4485 },
4486 write: |settings_content, value| {
4487 settings_content
4488 .project_panel
4489 .get_or_insert_default()
4490 .show_diagnostics = value;
4491 },
4492 }),
4493 metadata: None,
4494 files: USER,
4495 }),
4496 SettingsPageItem::SettingItem(SettingItem {
4497 title: "Sticky Scroll",
4498 description: "Whether to stick parent directories at top of the project panel.",
4499 field: Box::new(SettingField {
4500 json_path: Some("project_panel.sticky_scroll"),
4501 pick: |settings_content| {
4502 settings_content
4503 .project_panel
4504 .as_ref()?
4505 .sticky_scroll
4506 .as_ref()
4507 },
4508 write: |settings_content, value| {
4509 settings_content
4510 .project_panel
4511 .get_or_insert_default()
4512 .sticky_scroll = value;
4513 },
4514 }),
4515 metadata: None,
4516 files: USER,
4517 }),
4518 SettingsPageItem::SettingItem(SettingItem {
4519 files: USER,
4520 title: "Show Indent Guides",
4521 description: "Show indent guides in the project panel.",
4522 field: Box::new(SettingField {
4523 json_path: Some("project_panel.indent_guides.show"),
4524 pick: |settings_content| {
4525 settings_content
4526 .project_panel
4527 .as_ref()?
4528 .indent_guides
4529 .as_ref()?
4530 .show
4531 .as_ref()
4532 },
4533 write: |settings_content, value| {
4534 settings_content
4535 .project_panel
4536 .get_or_insert_default()
4537 .indent_guides
4538 .get_or_insert_default()
4539 .show = value;
4540 },
4541 }),
4542 metadata: None,
4543 }),
4544 SettingsPageItem::SettingItem(SettingItem {
4545 title: "Drag and Drop",
4546 description: "Whether to enable drag-and-drop operations in the project panel.",
4547 field: Box::new(SettingField {
4548 json_path: Some("project_panel.drag_and_drop"),
4549 pick: |settings_content| {
4550 settings_content
4551 .project_panel
4552 .as_ref()?
4553 .drag_and_drop
4554 .as_ref()
4555 },
4556 write: |settings_content, value| {
4557 settings_content
4558 .project_panel
4559 .get_or_insert_default()
4560 .drag_and_drop = value;
4561 },
4562 }),
4563 metadata: None,
4564 files: USER,
4565 }),
4566 SettingsPageItem::SettingItem(SettingItem {
4567 title: "Hide Root",
4568 description: "Whether to hide the root entry when only one folder is open in the window.",
4569 field: Box::new(SettingField {
4570 json_path: Some("project_panel.drag_and_drop"),
4571 pick: |settings_content| {
4572 settings_content.project_panel.as_ref()?.hide_root.as_ref()
4573 },
4574 write: |settings_content, value| {
4575 settings_content
4576 .project_panel
4577 .get_or_insert_default()
4578 .hide_root = value;
4579 },
4580 }),
4581 metadata: None,
4582 files: USER,
4583 }),
4584 SettingsPageItem::SettingItem(SettingItem {
4585 title: "Hide Hidden",
4586 description: "Whether to hide the hidden entries in the project panel.",
4587 field: Box::new(SettingField {
4588 json_path: Some("project_panel.hide_hidden"),
4589 pick: |settings_content| {
4590 settings_content
4591 .project_panel
4592 .as_ref()?
4593 .hide_hidden
4594 .as_ref()
4595 },
4596 write: |settings_content, value| {
4597 settings_content
4598 .project_panel
4599 .get_or_insert_default()
4600 .hide_hidden = value;
4601 },
4602 }),
4603 metadata: None,
4604 files: USER,
4605 }),
4606 SettingsPageItem::SettingItem(SettingItem {
4607 title: "Hidden Files",
4608 description: "Globs to match files that will be considered \"hidden\" and can be hidden from the project panel.",
4609 field: Box::new(
4610 SettingField {
4611 json_path: Some("worktree.hidden_files"),
4612 pick: |settings_content| {
4613 settings_content.project.worktree.hidden_files.as_ref()
4614 },
4615 write: |settings_content, value| {
4616 settings_content.project.worktree.hidden_files = value;
4617 },
4618 }
4619 .unimplemented(),
4620 ),
4621 metadata: None,
4622 files: USER,
4623 }),
4624 ]
4625 }
4626
4627 fn auto_open_files_section() -> [SettingsPageItem; 5] {
4628 [
4629 SettingsPageItem::SectionHeader("Auto Open Files"),
4630 SettingsPageItem::SettingItem(SettingItem {
4631 title: "On Create",
4632 description: "Whether to automatically open newly created files in the editor.",
4633 field: Box::new(SettingField {
4634 json_path: Some("project_panel.auto_open.on_create"),
4635 pick: |settings_content| {
4636 settings_content
4637 .project_panel
4638 .as_ref()?
4639 .auto_open
4640 .as_ref()?
4641 .on_create
4642 .as_ref()
4643 },
4644 write: |settings_content, value| {
4645 settings_content
4646 .project_panel
4647 .get_or_insert_default()
4648 .auto_open
4649 .get_or_insert_default()
4650 .on_create = value;
4651 },
4652 }),
4653 metadata: None,
4654 files: USER,
4655 }),
4656 SettingsPageItem::SettingItem(SettingItem {
4657 title: "On Paste",
4658 description: "Whether to automatically open files after pasting or duplicating them.",
4659 field: Box::new(SettingField {
4660 json_path: Some("project_panel.auto_open.on_paste"),
4661 pick: |settings_content| {
4662 settings_content
4663 .project_panel
4664 .as_ref()?
4665 .auto_open
4666 .as_ref()?
4667 .on_paste
4668 .as_ref()
4669 },
4670 write: |settings_content, value| {
4671 settings_content
4672 .project_panel
4673 .get_or_insert_default()
4674 .auto_open
4675 .get_or_insert_default()
4676 .on_paste = value;
4677 },
4678 }),
4679 metadata: None,
4680 files: USER,
4681 }),
4682 SettingsPageItem::SettingItem(SettingItem {
4683 title: "On Drop",
4684 description: "Whether to automatically open files dropped from external sources.",
4685 field: Box::new(SettingField {
4686 json_path: Some("project_panel.auto_open.on_drop"),
4687 pick: |settings_content| {
4688 settings_content
4689 .project_panel
4690 .as_ref()?
4691 .auto_open
4692 .as_ref()?
4693 .on_drop
4694 .as_ref()
4695 },
4696 write: |settings_content, value| {
4697 settings_content
4698 .project_panel
4699 .get_or_insert_default()
4700 .auto_open
4701 .get_or_insert_default()
4702 .on_drop = value;
4703 },
4704 }),
4705 metadata: None,
4706 files: USER,
4707 }),
4708 SettingsPageItem::SettingItem(SettingItem {
4709 title: "Sort Mode",
4710 description: "Sort order for entries in the project panel.",
4711 field: Box::new(SettingField {
4712 pick: |settings_content| {
4713 settings_content.project_panel.as_ref()?.sort_mode.as_ref()
4714 },
4715 write: |settings_content, value| {
4716 settings_content
4717 .project_panel
4718 .get_or_insert_default()
4719 .sort_mode = value;
4720 },
4721 json_path: Some("project_panel.sort_mode"),
4722 }),
4723 metadata: None,
4724 files: USER,
4725 }),
4726 ]
4727 }
4728
4729 fn terminal_panel_section() -> [SettingsPageItem; 2] {
4730 [
4731 SettingsPageItem::SectionHeader("Terminal Panel"),
4732 SettingsPageItem::SettingItem(SettingItem {
4733 title: "Terminal Dock",
4734 description: "Where to dock the terminal panel.",
4735 field: Box::new(SettingField {
4736 json_path: Some("terminal.dock"),
4737 pick: |settings_content| settings_content.terminal.as_ref()?.dock.as_ref(),
4738 write: |settings_content, value| {
4739 settings_content.terminal.get_or_insert_default().dock = value;
4740 },
4741 }),
4742 metadata: None,
4743 files: USER,
4744 }),
4745 ]
4746 }
4747
4748 fn outline_panel_section() -> [SettingsPageItem; 11] {
4749 [
4750 SettingsPageItem::SectionHeader("Outline Panel"),
4751 SettingsPageItem::SettingItem(SettingItem {
4752 title: "Outline Panel Button",
4753 description: "Show the outline panel button in the status bar.",
4754 field: Box::new(SettingField {
4755 json_path: Some("outline_panel.button"),
4756 pick: |settings_content| {
4757 settings_content.outline_panel.as_ref()?.button.as_ref()
4758 },
4759 write: |settings_content, value| {
4760 settings_content
4761 .outline_panel
4762 .get_or_insert_default()
4763 .button = value;
4764 },
4765 }),
4766 metadata: None,
4767 files: USER,
4768 }),
4769 SettingsPageItem::SettingItem(SettingItem {
4770 title: "Outline Panel Dock",
4771 description: "Where to dock the outline panel.",
4772 field: Box::new(SettingField {
4773 json_path: Some("outline_panel.dock"),
4774 pick: |settings_content| settings_content.outline_panel.as_ref()?.dock.as_ref(),
4775 write: |settings_content, value| {
4776 settings_content.outline_panel.get_or_insert_default().dock = value;
4777 },
4778 }),
4779 metadata: None,
4780 files: USER,
4781 }),
4782 SettingsPageItem::SettingItem(SettingItem {
4783 title: "Outline Panel Default Width",
4784 description: "Default width of the outline panel in pixels.",
4785 field: Box::new(SettingField {
4786 json_path: Some("outline_panel.default_width"),
4787 pick: |settings_content| {
4788 settings_content
4789 .outline_panel
4790 .as_ref()?
4791 .default_width
4792 .as_ref()
4793 },
4794 write: |settings_content, value| {
4795 settings_content
4796 .outline_panel
4797 .get_or_insert_default()
4798 .default_width = value;
4799 },
4800 }),
4801 metadata: None,
4802 files: USER,
4803 }),
4804 SettingsPageItem::SettingItem(SettingItem {
4805 title: "File Icons",
4806 description: "Show file icons in the outline panel.",
4807 field: Box::new(SettingField {
4808 json_path: Some("outline_panel.file_icons"),
4809 pick: |settings_content| {
4810 settings_content.outline_panel.as_ref()?.file_icons.as_ref()
4811 },
4812 write: |settings_content, value| {
4813 settings_content
4814 .outline_panel
4815 .get_or_insert_default()
4816 .file_icons = value;
4817 },
4818 }),
4819 metadata: None,
4820 files: USER,
4821 }),
4822 SettingsPageItem::SettingItem(SettingItem {
4823 title: "Folder Icons",
4824 description: "Whether to show folder icons or chevrons for directories in the outline panel.",
4825 field: Box::new(SettingField {
4826 json_path: Some("outline_panel.folder_icons"),
4827 pick: |settings_content| {
4828 settings_content
4829 .outline_panel
4830 .as_ref()?
4831 .folder_icons
4832 .as_ref()
4833 },
4834 write: |settings_content, value| {
4835 settings_content
4836 .outline_panel
4837 .get_or_insert_default()
4838 .folder_icons = value;
4839 },
4840 }),
4841 metadata: None,
4842 files: USER,
4843 }),
4844 SettingsPageItem::SettingItem(SettingItem {
4845 title: "Git Status",
4846 description: "Show the Git status in the outline panel.",
4847 field: Box::new(SettingField {
4848 json_path: Some("outline_panel.git_status"),
4849 pick: |settings_content| {
4850 settings_content.outline_panel.as_ref()?.git_status.as_ref()
4851 },
4852 write: |settings_content, value| {
4853 settings_content
4854 .outline_panel
4855 .get_or_insert_default()
4856 .git_status = value;
4857 },
4858 }),
4859 metadata: None,
4860 files: USER,
4861 }),
4862 SettingsPageItem::SettingItem(SettingItem {
4863 title: "Indent Size",
4864 description: "Amount of indentation for nested items.",
4865 field: Box::new(SettingField {
4866 json_path: Some("outline_panel.indent_size"),
4867 pick: |settings_content| {
4868 settings_content
4869 .outline_panel
4870 .as_ref()?
4871 .indent_size
4872 .as_ref()
4873 },
4874 write: |settings_content, value| {
4875 settings_content
4876 .outline_panel
4877 .get_or_insert_default()
4878 .indent_size = value;
4879 },
4880 }),
4881 metadata: None,
4882 files: USER,
4883 }),
4884 SettingsPageItem::SettingItem(SettingItem {
4885 title: "Auto Reveal Entries",
4886 description: "Whether to reveal when a corresponding outline entry becomes active.",
4887 field: Box::new(SettingField {
4888 json_path: Some("outline_panel.auto_reveal_entries"),
4889 pick: |settings_content| {
4890 settings_content
4891 .outline_panel
4892 .as_ref()?
4893 .auto_reveal_entries
4894 .as_ref()
4895 },
4896 write: |settings_content, value| {
4897 settings_content
4898 .outline_panel
4899 .get_or_insert_default()
4900 .auto_reveal_entries = value;
4901 },
4902 }),
4903 metadata: None,
4904 files: USER,
4905 }),
4906 SettingsPageItem::SettingItem(SettingItem {
4907 title: "Auto Fold Directories",
4908 description: "Whether to fold directories automatically when a directory contains only one subdirectory.",
4909 field: Box::new(SettingField {
4910 json_path: Some("outline_panel.auto_fold_dirs"),
4911 pick: |settings_content| {
4912 settings_content
4913 .outline_panel
4914 .as_ref()?
4915 .auto_fold_dirs
4916 .as_ref()
4917 },
4918 write: |settings_content, value| {
4919 settings_content
4920 .outline_panel
4921 .get_or_insert_default()
4922 .auto_fold_dirs = value;
4923 },
4924 }),
4925 metadata: None,
4926 files: USER,
4927 }),
4928 SettingsPageItem::SettingItem(SettingItem {
4929 files: USER,
4930 title: "Show Indent Guides",
4931 description: "When to show indent guides in the outline panel.",
4932 field: Box::new(SettingField {
4933 json_path: Some("outline_panel.indent_guides.show"),
4934 pick: |settings_content| {
4935 settings_content
4936 .outline_panel
4937 .as_ref()?
4938 .indent_guides
4939 .as_ref()?
4940 .show
4941 .as_ref()
4942 },
4943 write: |settings_content, value| {
4944 settings_content
4945 .outline_panel
4946 .get_or_insert_default()
4947 .indent_guides
4948 .get_or_insert_default()
4949 .show = value;
4950 },
4951 }),
4952 metadata: None,
4953 }),
4954 ]
4955 }
4956
4957 fn git_panel_section() -> [SettingsPageItem; 10] {
4958 [
4959 SettingsPageItem::SectionHeader("Git Panel"),
4960 SettingsPageItem::SettingItem(SettingItem {
4961 title: "Git Panel Button",
4962 description: "Show the Git panel button in the status bar.",
4963 field: Box::new(SettingField {
4964 json_path: Some("git_panel.button"),
4965 pick: |settings_content| settings_content.git_panel.as_ref()?.button.as_ref(),
4966 write: |settings_content, value| {
4967 settings_content.git_panel.get_or_insert_default().button = value;
4968 },
4969 }),
4970 metadata: None,
4971 files: USER,
4972 }),
4973 SettingsPageItem::SettingItem(SettingItem {
4974 title: "Git Panel Dock",
4975 description: "Where to dock the Git panel.",
4976 field: Box::new(SettingField {
4977 json_path: Some("git_panel.dock"),
4978 pick: |settings_content| settings_content.git_panel.as_ref()?.dock.as_ref(),
4979 write: |settings_content, value| {
4980 settings_content.git_panel.get_or_insert_default().dock = value;
4981 },
4982 }),
4983 metadata: None,
4984 files: USER,
4985 }),
4986 SettingsPageItem::SettingItem(SettingItem {
4987 title: "Git Panel Default Width",
4988 description: "Default width of the Git panel in pixels.",
4989 field: Box::new(SettingField {
4990 json_path: Some("git_panel.default_width"),
4991 pick: |settings_content| {
4992 settings_content.git_panel.as_ref()?.default_width.as_ref()
4993 },
4994 write: |settings_content, value| {
4995 settings_content
4996 .git_panel
4997 .get_or_insert_default()
4998 .default_width = value;
4999 },
5000 }),
5001 metadata: None,
5002 files: USER,
5003 }),
5004 SettingsPageItem::SettingItem(SettingItem {
5005 title: "Git Panel Status Style",
5006 description: "How entry statuses are displayed.",
5007 field: Box::new(SettingField {
5008 json_path: Some("git_panel.status_style"),
5009 pick: |settings_content| {
5010 settings_content.git_panel.as_ref()?.status_style.as_ref()
5011 },
5012 write: |settings_content, value| {
5013 settings_content
5014 .git_panel
5015 .get_or_insert_default()
5016 .status_style = value;
5017 },
5018 }),
5019 metadata: None,
5020 files: USER,
5021 }),
5022 SettingsPageItem::SettingItem(SettingItem {
5023 title: "Fallback Branch Name",
5024 description: "Default branch name will be when init.defaultbranch is not set in Git.",
5025 field: Box::new(SettingField {
5026 json_path: Some("git_panel.fallback_branch_name"),
5027 pick: |settings_content| {
5028 settings_content
5029 .git_panel
5030 .as_ref()?
5031 .fallback_branch_name
5032 .as_ref()
5033 },
5034 write: |settings_content, value| {
5035 settings_content
5036 .git_panel
5037 .get_or_insert_default()
5038 .fallback_branch_name = value;
5039 },
5040 }),
5041 metadata: None,
5042 files: USER,
5043 }),
5044 SettingsPageItem::SettingItem(SettingItem {
5045 title: "Sort By Path",
5046 description: "Enable to sort entries in the panel by path, disable to sort by status.",
5047 field: Box::new(SettingField {
5048 json_path: Some("git_panel.sort_by_path"),
5049 pick: |settings_content| {
5050 settings_content.git_panel.as_ref()?.sort_by_path.as_ref()
5051 },
5052 write: |settings_content, value| {
5053 settings_content
5054 .git_panel
5055 .get_or_insert_default()
5056 .sort_by_path = value;
5057 },
5058 }),
5059 metadata: None,
5060 files: USER,
5061 }),
5062 SettingsPageItem::SettingItem(SettingItem {
5063 title: "Collapse Untracked Diff",
5064 description: "Whether to collapse untracked files in the diff panel.",
5065 field: Box::new(SettingField {
5066 json_path: Some("git_panel.collapse_untracked_diff"),
5067 pick: |settings_content| {
5068 settings_content
5069 .git_panel
5070 .as_ref()?
5071 .collapse_untracked_diff
5072 .as_ref()
5073 },
5074 write: |settings_content, value| {
5075 settings_content
5076 .git_panel
5077 .get_or_insert_default()
5078 .collapse_untracked_diff = value;
5079 },
5080 }),
5081 metadata: None,
5082 files: USER,
5083 }),
5084 SettingsPageItem::SettingItem(SettingItem {
5085 title: "Tree View",
5086 description: "Enable to show entries in tree view list, disable to show in flat view list.",
5087 field: Box::new(SettingField {
5088 json_path: Some("git_panel.tree_view"),
5089 pick: |settings_content| {
5090 settings_content.git_panel.as_ref()?.tree_view.as_ref()
5091 },
5092 write: |settings_content, value| {
5093 settings_content.git_panel.get_or_insert_default().tree_view = value;
5094 },
5095 }),
5096 metadata: None,
5097 files: USER,
5098 }),
5099 SettingsPageItem::SettingItem(SettingItem {
5100 title: "Scroll Bar",
5101 description: "How and when the scrollbar should be displayed.",
5102 field: Box::new(SettingField {
5103 json_path: Some("git_panel.scrollbar.show"),
5104 pick: |settings_content| {
5105 show_scrollbar_or_editor(settings_content, |settings_content| {
5106 settings_content
5107 .git_panel
5108 .as_ref()?
5109 .scrollbar
5110 .as_ref()?
5111 .show
5112 .as_ref()
5113 })
5114 },
5115 write: |settings_content, value| {
5116 settings_content
5117 .git_panel
5118 .get_or_insert_default()
5119 .scrollbar
5120 .get_or_insert_default()
5121 .show = value;
5122 },
5123 }),
5124 metadata: None,
5125 files: USER,
5126 }),
5127 ]
5128 }
5129
5130 fn debugger_panel_section() -> [SettingsPageItem; 2] {
5131 [
5132 SettingsPageItem::SectionHeader("Debugger Panel"),
5133 SettingsPageItem::SettingItem(SettingItem {
5134 title: "Debugger Panel Dock",
5135 description: "The dock position of the debug panel.",
5136 field: Box::new(SettingField {
5137 json_path: Some("debugger.dock"),
5138 pick: |settings_content| settings_content.debugger.as_ref()?.dock.as_ref(),
5139 write: |settings_content, value| {
5140 settings_content.debugger.get_or_insert_default().dock = value;
5141 },
5142 }),
5143 metadata: None,
5144 files: USER,
5145 }),
5146 ]
5147 }
5148
5149 fn notification_panel_section() -> [SettingsPageItem; 4] {
5150 [
5151 SettingsPageItem::SectionHeader("Notification Panel"),
5152 SettingsPageItem::SettingItem(SettingItem {
5153 title: "Notification Panel Button",
5154 description: "Show the notification panel button in the status bar.",
5155 field: Box::new(SettingField {
5156 json_path: Some("notification_panel.button"),
5157 pick: |settings_content| {
5158 settings_content
5159 .notification_panel
5160 .as_ref()?
5161 .button
5162 .as_ref()
5163 },
5164 write: |settings_content, value| {
5165 settings_content
5166 .notification_panel
5167 .get_or_insert_default()
5168 .button = value;
5169 },
5170 }),
5171 metadata: None,
5172 files: USER,
5173 }),
5174 SettingsPageItem::SettingItem(SettingItem {
5175 title: "Notification Panel Dock",
5176 description: "Where to dock the notification panel.",
5177 field: Box::new(SettingField {
5178 json_path: Some("notification_panel.dock"),
5179 pick: |settings_content| {
5180 settings_content.notification_panel.as_ref()?.dock.as_ref()
5181 },
5182 write: |settings_content, value| {
5183 settings_content
5184 .notification_panel
5185 .get_or_insert_default()
5186 .dock = value;
5187 },
5188 }),
5189 metadata: None,
5190 files: USER,
5191 }),
5192 SettingsPageItem::SettingItem(SettingItem {
5193 title: "Notification Panel Default Width",
5194 description: "Default width of the notification panel in pixels.",
5195 field: Box::new(SettingField {
5196 json_path: Some("notification_panel.default_width"),
5197 pick: |settings_content| {
5198 settings_content
5199 .notification_panel
5200 .as_ref()?
5201 .default_width
5202 .as_ref()
5203 },
5204 write: |settings_content, value| {
5205 settings_content
5206 .notification_panel
5207 .get_or_insert_default()
5208 .default_width = value;
5209 },
5210 }),
5211 metadata: None,
5212 files: USER,
5213 }),
5214 ]
5215 }
5216
5217 fn collaboration_panel_section() -> [SettingsPageItem; 4] {
5218 [
5219 SettingsPageItem::SectionHeader("Collaboration Panel"),
5220 SettingsPageItem::SettingItem(SettingItem {
5221 title: "Collaboration Panel Button",
5222 description: "Show the collaboration panel button in the status bar.",
5223 field: Box::new(SettingField {
5224 json_path: Some("collaboration_panel.button"),
5225 pick: |settings_content| {
5226 settings_content
5227 .collaboration_panel
5228 .as_ref()?
5229 .button
5230 .as_ref()
5231 },
5232 write: |settings_content, value| {
5233 settings_content
5234 .collaboration_panel
5235 .get_or_insert_default()
5236 .button = value;
5237 },
5238 }),
5239 metadata: None,
5240 files: USER,
5241 }),
5242 SettingsPageItem::SettingItem(SettingItem {
5243 title: "Collaboration Panel Dock",
5244 description: "Where to dock the collaboration panel.",
5245 field: Box::new(SettingField {
5246 json_path: Some("collaboration_panel.dock"),
5247 pick: |settings_content| {
5248 settings_content.collaboration_panel.as_ref()?.dock.as_ref()
5249 },
5250 write: |settings_content, value| {
5251 settings_content
5252 .collaboration_panel
5253 .get_or_insert_default()
5254 .dock = value;
5255 },
5256 }),
5257 metadata: None,
5258 files: USER,
5259 }),
5260 SettingsPageItem::SettingItem(SettingItem {
5261 title: "Collaboration Panel Default Width",
5262 description: "Default width of the collaboration panel in pixels.",
5263 field: Box::new(SettingField {
5264 json_path: Some("collaboration_panel.dock"),
5265 pick: |settings_content| {
5266 settings_content
5267 .collaboration_panel
5268 .as_ref()?
5269 .default_width
5270 .as_ref()
5271 },
5272 write: |settings_content, value| {
5273 settings_content
5274 .collaboration_panel
5275 .get_or_insert_default()
5276 .default_width = value;
5277 },
5278 }),
5279 metadata: None,
5280 files: USER,
5281 }),
5282 ]
5283 }
5284
5285 fn agent_panel_section() -> [SettingsPageItem; 5] {
5286 [
5287 SettingsPageItem::SectionHeader("Agent Panel"),
5288 SettingsPageItem::SettingItem(SettingItem {
5289 title: "Agent Panel Button",
5290 description: "Whether to show the agent panel button in the status bar.",
5291 field: Box::new(SettingField {
5292 json_path: Some("agent.button"),
5293 pick: |settings_content| settings_content.agent.as_ref()?.button.as_ref(),
5294 write: |settings_content, value| {
5295 settings_content.agent.get_or_insert_default().button = value;
5296 },
5297 }),
5298 metadata: None,
5299 files: USER,
5300 }),
5301 SettingsPageItem::SettingItem(SettingItem {
5302 title: "Agent Panel Dock",
5303 description: "Where to dock the agent panel.",
5304 field: Box::new(SettingField {
5305 json_path: Some("agent.dock"),
5306 pick: |settings_content| settings_content.agent.as_ref()?.dock.as_ref(),
5307 write: |settings_content, value| {
5308 settings_content.agent.get_or_insert_default().dock = value;
5309 },
5310 }),
5311 metadata: None,
5312 files: USER,
5313 }),
5314 SettingsPageItem::SettingItem(SettingItem {
5315 title: "Agent Panel Default Width",
5316 description: "Default width when the agent panel is docked to the left or right.",
5317 field: Box::new(SettingField {
5318 json_path: Some("agent.default_width"),
5319 pick: |settings_content| {
5320 settings_content.agent.as_ref()?.default_width.as_ref()
5321 },
5322 write: |settings_content, value| {
5323 settings_content.agent.get_or_insert_default().default_width = value;
5324 },
5325 }),
5326 metadata: None,
5327 files: USER,
5328 }),
5329 SettingsPageItem::SettingItem(SettingItem {
5330 title: "Agent Panel Default Height",
5331 description: "Default height when the agent panel is docked to the bottom.",
5332 field: Box::new(SettingField {
5333 json_path: Some("agent.default_height"),
5334 pick: |settings_content| {
5335 settings_content.agent.as_ref()?.default_height.as_ref()
5336 },
5337 write: |settings_content, value| {
5338 settings_content
5339 .agent
5340 .get_or_insert_default()
5341 .default_height = value;
5342 },
5343 }),
5344 metadata: None,
5345 files: USER,
5346 }),
5347 ]
5348 }
5349
5350 SettingsPage {
5351 title: "Panels",
5352 items: concat_sections![
5353 project_panel_section(),
5354 auto_open_files_section(),
5355 terminal_panel_section(),
5356 outline_panel_section(),
5357 git_panel_section(),
5358 debugger_panel_section(),
5359 notification_panel_section(),
5360 collaboration_panel_section(),
5361 agent_panel_section(),
5362 ],
5363 }
5364}
5365
5366fn debugger_page() -> SettingsPage {
5367 fn general_section() -> [SettingsPageItem; 6] {
5368 [
5369 SettingsPageItem::SectionHeader("General"),
5370 SettingsPageItem::SettingItem(SettingItem {
5371 title: "Stepping Granularity",
5372 description: "Determines the stepping granularity for debug operations.",
5373 field: Box::new(SettingField {
5374 json_path: Some("debugger.stepping_granularity"),
5375 pick: |settings_content| {
5376 settings_content
5377 .debugger
5378 .as_ref()?
5379 .stepping_granularity
5380 .as_ref()
5381 },
5382 write: |settings_content, value| {
5383 settings_content
5384 .debugger
5385 .get_or_insert_default()
5386 .stepping_granularity = value;
5387 },
5388 }),
5389 metadata: None,
5390 files: USER,
5391 }),
5392 SettingsPageItem::SettingItem(SettingItem {
5393 title: "Save Breakpoints",
5394 description: "Whether breakpoints should be reused across Zed sessions.",
5395 field: Box::new(SettingField {
5396 json_path: Some("debugger.save_breakpoints"),
5397 pick: |settings_content| {
5398 settings_content
5399 .debugger
5400 .as_ref()?
5401 .save_breakpoints
5402 .as_ref()
5403 },
5404 write: |settings_content, value| {
5405 settings_content
5406 .debugger
5407 .get_or_insert_default()
5408 .save_breakpoints = value;
5409 },
5410 }),
5411 metadata: None,
5412 files: USER,
5413 }),
5414 SettingsPageItem::SettingItem(SettingItem {
5415 title: "Timeout",
5416 description: "Time in milliseconds until timeout error when connecting to a TCP debug adapter.",
5417 field: Box::new(SettingField {
5418 json_path: Some("debugger.timeout"),
5419 pick: |settings_content| settings_content.debugger.as_ref()?.timeout.as_ref(),
5420 write: |settings_content, value| {
5421 settings_content.debugger.get_or_insert_default().timeout = value;
5422 },
5423 }),
5424 metadata: None,
5425 files: USER,
5426 }),
5427 SettingsPageItem::SettingItem(SettingItem {
5428 title: "Log DAP Communications",
5429 description: "Whether to log messages between active debug adapters and Zed.",
5430 field: Box::new(SettingField {
5431 json_path: Some("debugger.log_dap_communications"),
5432 pick: |settings_content| {
5433 settings_content
5434 .debugger
5435 .as_ref()?
5436 .log_dap_communications
5437 .as_ref()
5438 },
5439 write: |settings_content, value| {
5440 settings_content
5441 .debugger
5442 .get_or_insert_default()
5443 .log_dap_communications = value;
5444 },
5445 }),
5446 metadata: None,
5447 files: USER,
5448 }),
5449 SettingsPageItem::SettingItem(SettingItem {
5450 title: "Format DAP Log Messages",
5451 description: "Whether to format DAP messages when adding them to debug adapter logger.",
5452 field: Box::new(SettingField {
5453 json_path: Some("debugger.format_dap_log_messages"),
5454 pick: |settings_content| {
5455 settings_content
5456 .debugger
5457 .as_ref()?
5458 .format_dap_log_messages
5459 .as_ref()
5460 },
5461 write: |settings_content, value| {
5462 settings_content
5463 .debugger
5464 .get_or_insert_default()
5465 .format_dap_log_messages = value;
5466 },
5467 }),
5468 metadata: None,
5469 files: USER,
5470 }),
5471 ]
5472 }
5473
5474 SettingsPage {
5475 title: "Debugger",
5476 items: concat_sections![general_section()],
5477 }
5478}
5479
5480fn terminal_page() -> SettingsPage {
5481 fn environment_section() -> [SettingsPageItem; 5] {
5482 [
5483 SettingsPageItem::SectionHeader("Environment"),
5484 SettingsPageItem::DynamicItem(DynamicItem {
5485 discriminant: SettingItem {
5486 files: USER | PROJECT,
5487 title: "Shell",
5488 description: "What shell to use when opening a terminal.",
5489 field: Box::new(SettingField {
5490 json_path: Some("terminal.shell$"),
5491 pick: |settings_content| {
5492 Some(&dynamic_variants::<settings::Shell>()[
5493 settings_content
5494 .terminal
5495 .as_ref()?
5496 .project
5497 .shell
5498 .as_ref()?
5499 .discriminant() as usize
5500 ])
5501 },
5502 write: |settings_content, value| {
5503 let Some(value) = value else {
5504 if let Some(terminal) = settings_content.terminal.as_mut() {
5505 terminal.project.shell = None;
5506 }
5507 return;
5508 };
5509 let settings_value = settings_content
5510 .terminal
5511 .get_or_insert_default()
5512 .project
5513 .shell
5514 .get_or_insert_with(|| settings::Shell::default());
5515 let default_shell = if cfg!(target_os = "windows") {
5516 "powershell.exe"
5517 } else {
5518 "sh"
5519 };
5520 *settings_value = match value {
5521 settings::ShellDiscriminants::System => settings::Shell::System,
5522 settings::ShellDiscriminants::Program => {
5523 let program = match settings_value {
5524 settings::Shell::Program(program) => program.clone(),
5525 settings::Shell::WithArguments { program, .. } => program.clone(),
5526 _ => String::from(default_shell),
5527 };
5528 settings::Shell::Program(program)
5529 }
5530 settings::ShellDiscriminants::WithArguments => {
5531 let (program, args, title_override) = match settings_value {
5532 settings::Shell::Program(program) => (program.clone(), vec![], None),
5533 settings::Shell::WithArguments {
5534 program,
5535 args,
5536 title_override,
5537 } => (program.clone(), args.clone(), title_override.clone()),
5538 _ => (String::from(default_shell), vec![], None),
5539 };
5540 settings::Shell::WithArguments {
5541 program,
5542 args,
5543 title_override,
5544 }
5545 }
5546 };
5547 },
5548 }),
5549 metadata: None,
5550 },
5551 pick_discriminant: |settings_content| {
5552 Some(
5553 settings_content
5554 .terminal
5555 .as_ref()?
5556 .project
5557 .shell
5558 .as_ref()?
5559 .discriminant() as usize,
5560 )
5561 },
5562 fields: dynamic_variants::<settings::Shell>()
5563 .into_iter()
5564 .map(|variant| match variant {
5565 settings::ShellDiscriminants::System => vec![],
5566 settings::ShellDiscriminants::Program => vec![SettingItem {
5567 files: USER | PROJECT,
5568 title: "Program",
5569 description: "The shell program to use.",
5570 field: Box::new(SettingField {
5571 json_path: Some("terminal.shell"),
5572 pick: |settings_content| match settings_content.terminal.as_ref()?.project.shell.as_ref()
5573 {
5574 Some(settings::Shell::Program(program)) => Some(program),
5575 _ => None,
5576 },
5577 write: |settings_content, value| {
5578 let Some(value) = value else {
5579 return;
5580 };
5581 match settings_content
5582 .terminal
5583 .get_or_insert_default()
5584 .project
5585 .shell
5586 .as_mut()
5587 {
5588 Some(settings::Shell::Program(program)) => *program = value,
5589 _ => return,
5590 }
5591 },
5592 }),
5593 metadata: None,
5594 }],
5595 settings::ShellDiscriminants::WithArguments => vec![
5596 SettingItem {
5597 files: USER | PROJECT,
5598 title: "Program",
5599 description: "The shell program to run.",
5600 field: Box::new(SettingField {
5601 json_path: Some("terminal.shell.program"),
5602 pick: |settings_content| {
5603 match settings_content.terminal.as_ref()?.project.shell.as_ref() {
5604 Some(settings::Shell::WithArguments { program, .. }) => Some(program),
5605 _ => None,
5606 }
5607 },
5608 write: |settings_content, value| {
5609 let Some(value) = value else {
5610 return;
5611 };
5612 match settings_content
5613 .terminal
5614 .get_or_insert_default()
5615 .project
5616 .shell
5617 .as_mut()
5618 {
5619 Some(settings::Shell::WithArguments { program, .. }) => {
5620 *program = value
5621 }
5622 _ => return,
5623 }
5624 },
5625 }),
5626 metadata: None,
5627 },
5628 SettingItem {
5629 files: USER | PROJECT,
5630 title: "Arguments",
5631 description: "The arguments to pass to the shell program.",
5632 field: Box::new(
5633 SettingField {
5634 json_path: Some("terminal.shell.args"),
5635 pick: |settings_content| {
5636 match settings_content.terminal.as_ref()?.project.shell.as_ref() {
5637 Some(settings::Shell::WithArguments { args, .. }) => Some(args),
5638 _ => None,
5639 }
5640 },
5641 write: |settings_content, value| {
5642 let Some(value) = value else {
5643 return;
5644 };
5645 match settings_content
5646 .terminal
5647 .get_or_insert_default()
5648 .project
5649 .shell
5650 .as_mut()
5651 {
5652 Some(settings::Shell::WithArguments { args, .. }) => *args = value,
5653 _ => return,
5654 }
5655 },
5656 }
5657 .unimplemented(),
5658 ),
5659 metadata: None,
5660 },
5661 SettingItem {
5662 files: USER | PROJECT,
5663 title: "Title Override",
5664 description: "An optional string to override the title of the terminal tab.",
5665 field: Box::new(SettingField {
5666 json_path: Some("terminal.shell.title_override"),
5667 pick: |settings_content| {
5668 match settings_content.terminal.as_ref()?.project.shell.as_ref() {
5669 Some(settings::Shell::WithArguments { title_override, .. }) => {
5670 title_override.as_ref().or(DEFAULT_EMPTY_SHARED_STRING)
5671 }
5672 _ => None,
5673 }
5674 },
5675 write: |settings_content, value| {
5676 match settings_content
5677 .terminal
5678 .get_or_insert_default()
5679 .project
5680 .shell
5681 .as_mut()
5682 {
5683 Some(settings::Shell::WithArguments { title_override, .. }) => {
5684 *title_override = value.filter(|s| !s.is_empty())
5685 }
5686 _ => return,
5687 }
5688 },
5689 }),
5690 metadata: None,
5691 },
5692 ],
5693 })
5694 .collect(),
5695 }),
5696 SettingsPageItem::DynamicItem(DynamicItem {
5697 discriminant: SettingItem {
5698 files: USER | PROJECT,
5699 title: "Working Directory",
5700 description: "What working directory to use when launching the terminal.",
5701 field: Box::new(SettingField {
5702 json_path: Some("terminal.working_directory$"),
5703 pick: |settings_content| {
5704 Some(&dynamic_variants::<settings::WorkingDirectory>()[
5705 settings_content
5706 .terminal
5707 .as_ref()?
5708 .project
5709 .working_directory
5710 .as_ref()?
5711 .discriminant() as usize
5712 ])
5713 },
5714 write: |settings_content, value| {
5715 let Some(value) = value else {
5716 if let Some(terminal) = settings_content.terminal.as_mut() {
5717 terminal.project.working_directory = None;
5718 }
5719 return;
5720 };
5721 let settings_value = settings_content
5722 .terminal
5723 .get_or_insert_default()
5724 .project
5725 .working_directory
5726 .get_or_insert_with(|| settings::WorkingDirectory::CurrentProjectDirectory);
5727 *settings_value = match value {
5728 settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => {
5729 settings::WorkingDirectory::CurrentProjectDirectory
5730 }
5731 settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => {
5732 settings::WorkingDirectory::FirstProjectDirectory
5733 }
5734 settings::WorkingDirectoryDiscriminants::AlwaysHome => {
5735 settings::WorkingDirectory::AlwaysHome
5736 }
5737 settings::WorkingDirectoryDiscriminants::Always => {
5738 let directory = match settings_value {
5739 settings::WorkingDirectory::Always { .. } => return,
5740 _ => String::new(),
5741 };
5742 settings::WorkingDirectory::Always { directory }
5743 }
5744 };
5745 },
5746 }),
5747 metadata: None,
5748 },
5749 pick_discriminant: |settings_content| {
5750 Some(
5751 settings_content
5752 .terminal
5753 .as_ref()?
5754 .project
5755 .working_directory
5756 .as_ref()?
5757 .discriminant() as usize,
5758 )
5759 },
5760 fields: dynamic_variants::<settings::WorkingDirectory>()
5761 .into_iter()
5762 .map(|variant| match variant {
5763 settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => vec![],
5764 settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => vec![],
5765 settings::WorkingDirectoryDiscriminants::AlwaysHome => vec![],
5766 settings::WorkingDirectoryDiscriminants::Always => vec![SettingItem {
5767 files: USER | PROJECT,
5768 title: "Directory",
5769 description: "The directory path to use (will be shell expanded).",
5770 field: Box::new(SettingField {
5771 json_path: Some("terminal.working_directory.always"),
5772 pick: |settings_content| {
5773 match settings_content.terminal.as_ref()?.project.working_directory.as_ref() {
5774 Some(settings::WorkingDirectory::Always { directory }) => Some(directory),
5775 _ => None,
5776 }
5777 },
5778 write: |settings_content, value| {
5779 let value = value.unwrap_or_default();
5780 match settings_content
5781 .terminal
5782 .get_or_insert_default()
5783 .project
5784 .working_directory
5785 .as_mut()
5786 {
5787 Some(settings::WorkingDirectory::Always { directory }) => *directory = value,
5788 _ => return,
5789 }
5790 },
5791 }),
5792 metadata: None,
5793 }],
5794 })
5795 .collect(),
5796 }),
5797 SettingsPageItem::SettingItem(SettingItem {
5798 title: "Environment Variables",
5799 description: "Key-value pairs to add to the terminal's environment.",
5800 field: Box::new(
5801 SettingField {
5802 json_path: Some("terminal.env"),
5803 pick: |settings_content| settings_content.terminal.as_ref()?.project.env.as_ref(),
5804 write: |settings_content, value| {
5805 settings_content.terminal.get_or_insert_default().project.env = value;
5806 },
5807 }
5808 .unimplemented(),
5809 ),
5810 metadata: None,
5811 files: USER | PROJECT,
5812 }),
5813 SettingsPageItem::SettingItem(SettingItem {
5814 title: "Detect Virtual Environment",
5815 description: "Activates the Python virtual environment, if one is found, in the terminal's working directory.",
5816 field: Box::new(
5817 SettingField {
5818 json_path: Some("terminal.detect_venv"),
5819 pick: |settings_content| settings_content.terminal.as_ref()?.project.detect_venv.as_ref(),
5820 write: |settings_content, value| {
5821 settings_content
5822 .terminal
5823 .get_or_insert_default()
5824 .project
5825 .detect_venv = value;
5826 },
5827 }
5828 .unimplemented(),
5829 ),
5830 metadata: None,
5831 files: USER | PROJECT,
5832 }),
5833 ]
5834 }
5835
5836 fn font_section() -> [SettingsPageItem; 6] {
5837 [
5838 SettingsPageItem::SectionHeader("Font"),
5839 SettingsPageItem::SettingItem(SettingItem {
5840 title: "Font Size",
5841 description: "Font size for terminal text. If not set, defaults to buffer font size.",
5842 field: Box::new(SettingField {
5843 json_path: Some("terminal.font_size"),
5844 pick: |settings_content| {
5845 settings_content
5846 .terminal
5847 .as_ref()
5848 .and_then(|terminal| terminal.font_size.as_ref())
5849 .or(settings_content.theme.buffer_font_size.as_ref())
5850 },
5851 write: |settings_content, value| {
5852 settings_content.terminal.get_or_insert_default().font_size = value;
5853 },
5854 }),
5855 metadata: None,
5856 files: USER,
5857 }),
5858 SettingsPageItem::SettingItem(SettingItem {
5859 title: "Font Family",
5860 description: "Font family for terminal text. If not set, defaults to buffer font family.",
5861 field: Box::new(SettingField {
5862 json_path: Some("terminal.font_family"),
5863 pick: |settings_content| {
5864 settings_content
5865 .terminal
5866 .as_ref()
5867 .and_then(|terminal| terminal.font_family.as_ref())
5868 .or(settings_content.theme.buffer_font_family.as_ref())
5869 },
5870 write: |settings_content, value| {
5871 settings_content
5872 .terminal
5873 .get_or_insert_default()
5874 .font_family = value;
5875 },
5876 }),
5877 metadata: None,
5878 files: USER,
5879 }),
5880 SettingsPageItem::SettingItem(SettingItem {
5881 title: "Font Fallbacks",
5882 description: "Font fallbacks for terminal text. If not set, defaults to buffer font fallbacks.",
5883 field: Box::new(
5884 SettingField {
5885 json_path: Some("terminal.font_fallbacks"),
5886 pick: |settings_content| {
5887 settings_content
5888 .terminal
5889 .as_ref()
5890 .and_then(|terminal| terminal.font_fallbacks.as_ref())
5891 .or(settings_content.theme.buffer_font_fallbacks.as_ref())
5892 },
5893 write: |settings_content, value| {
5894 settings_content
5895 .terminal
5896 .get_or_insert_default()
5897 .font_fallbacks = value;
5898 },
5899 }
5900 .unimplemented(),
5901 ),
5902 metadata: None,
5903 files: USER,
5904 }),
5905 SettingsPageItem::SettingItem(SettingItem {
5906 title: "Font Weight",
5907 description: "Font weight for terminal text in CSS weight units (100-900).",
5908 field: Box::new(SettingField {
5909 json_path: Some("terminal.font_weight"),
5910 pick: |settings_content| {
5911 settings_content.terminal.as_ref()?.font_weight.as_ref()
5912 },
5913 write: |settings_content, value| {
5914 settings_content
5915 .terminal
5916 .get_or_insert_default()
5917 .font_weight = value;
5918 },
5919 }),
5920 metadata: None,
5921 files: USER,
5922 }),
5923 SettingsPageItem::SettingItem(SettingItem {
5924 title: "Font Features",
5925 description: "Font features for terminal text.",
5926 field: Box::new(
5927 SettingField {
5928 json_path: Some("terminal.font_features"),
5929 pick: |settings_content| {
5930 settings_content
5931 .terminal
5932 .as_ref()
5933 .and_then(|terminal| terminal.font_features.as_ref())
5934 .or(settings_content.theme.buffer_font_features.as_ref())
5935 },
5936 write: |settings_content, value| {
5937 settings_content
5938 .terminal
5939 .get_or_insert_default()
5940 .font_features = value;
5941 },
5942 }
5943 .unimplemented(),
5944 ),
5945 metadata: None,
5946 files: USER,
5947 }),
5948 ]
5949 }
5950
5951 fn display_settings_section() -> [SettingsPageItem; 6] {
5952 [
5953 SettingsPageItem::SectionHeader("Display Settings"),
5954 SettingsPageItem::SettingItem(SettingItem {
5955 title: "Line Height",
5956 description: "Line height for terminal text.",
5957 field: Box::new(
5958 SettingField {
5959 json_path: Some("terminal.line_height"),
5960 pick: |settings_content| {
5961 settings_content.terminal.as_ref()?.line_height.as_ref()
5962 },
5963 write: |settings_content, value| {
5964 settings_content
5965 .terminal
5966 .get_or_insert_default()
5967 .line_height = value;
5968 },
5969 }
5970 .unimplemented(),
5971 ),
5972 metadata: None,
5973 files: USER,
5974 }),
5975 SettingsPageItem::SettingItem(SettingItem {
5976 title: "Cursor Shape",
5977 description: "Default cursor shape for the terminal (bar, block, underline, or hollow).",
5978 field: Box::new(SettingField {
5979 json_path: Some("terminal.cursor_shape"),
5980 pick: |settings_content| {
5981 settings_content.terminal.as_ref()?.cursor_shape.as_ref()
5982 },
5983 write: |settings_content, value| {
5984 settings_content
5985 .terminal
5986 .get_or_insert_default()
5987 .cursor_shape = value;
5988 },
5989 }),
5990 metadata: None,
5991 files: USER,
5992 }),
5993 SettingsPageItem::SettingItem(SettingItem {
5994 title: "Cursor Blinking",
5995 description: "Sets the cursor blinking behavior in the terminal.",
5996 field: Box::new(SettingField {
5997 json_path: Some("terminal.blinking"),
5998 pick: |settings_content| settings_content.terminal.as_ref()?.blinking.as_ref(),
5999 write: |settings_content, value| {
6000 settings_content.terminal.get_or_insert_default().blinking = value;
6001 },
6002 }),
6003 metadata: None,
6004 files: USER,
6005 }),
6006 SettingsPageItem::SettingItem(SettingItem {
6007 title: "Alternate Scroll",
6008 description: "Whether alternate scroll mode is active by default (converts mouse scroll to arrow keys in apps like Vim).",
6009 field: Box::new(SettingField {
6010 json_path: Some("terminal.alternate_scroll"),
6011 pick: |settings_content| {
6012 settings_content
6013 .terminal
6014 .as_ref()?
6015 .alternate_scroll
6016 .as_ref()
6017 },
6018 write: |settings_content, value| {
6019 settings_content
6020 .terminal
6021 .get_or_insert_default()
6022 .alternate_scroll = value;
6023 },
6024 }),
6025 metadata: None,
6026 files: USER,
6027 }),
6028 SettingsPageItem::SettingItem(SettingItem {
6029 title: "Minimum Contrast",
6030 description: "The minimum APCA perceptual contrast between foreground and background colors (0-106).",
6031 field: Box::new(SettingField {
6032 json_path: Some("terminal.minimum_contrast"),
6033 pick: |settings_content| {
6034 settings_content
6035 .terminal
6036 .as_ref()?
6037 .minimum_contrast
6038 .as_ref()
6039 },
6040 write: |settings_content, value| {
6041 settings_content
6042 .terminal
6043 .get_or_insert_default()
6044 .minimum_contrast = value;
6045 },
6046 }),
6047 metadata: None,
6048 files: USER,
6049 }),
6050 ]
6051 }
6052
6053 fn behavior_settings_section() -> [SettingsPageItem; 4] {
6054 [
6055 SettingsPageItem::SectionHeader("Behavior Settings"),
6056 SettingsPageItem::SettingItem(SettingItem {
6057 title: "Option As Meta",
6058 description: "Whether the option key behaves as the meta key.",
6059 field: Box::new(SettingField {
6060 json_path: Some("terminal.option_as_meta"),
6061 pick: |settings_content| {
6062 settings_content.terminal.as_ref()?.option_as_meta.as_ref()
6063 },
6064 write: |settings_content, value| {
6065 settings_content
6066 .terminal
6067 .get_or_insert_default()
6068 .option_as_meta = value;
6069 },
6070 }),
6071 metadata: None,
6072 files: USER,
6073 }),
6074 SettingsPageItem::SettingItem(SettingItem {
6075 title: "Copy On Select",
6076 description: "Whether selecting text in the terminal automatically copies to the system clipboard.",
6077 field: Box::new(SettingField {
6078 json_path: Some("terminal.copy_on_select"),
6079 pick: |settings_content| {
6080 settings_content.terminal.as_ref()?.copy_on_select.as_ref()
6081 },
6082 write: |settings_content, value| {
6083 settings_content
6084 .terminal
6085 .get_or_insert_default()
6086 .copy_on_select = value;
6087 },
6088 }),
6089 metadata: None,
6090 files: USER,
6091 }),
6092 SettingsPageItem::SettingItem(SettingItem {
6093 title: "Keep Selection On Copy",
6094 description: "Whether to keep the text selection after copying it to the clipboard.",
6095 field: Box::new(SettingField {
6096 json_path: Some("terminal.keep_selection_on_copy"),
6097 pick: |settings_content| {
6098 settings_content
6099 .terminal
6100 .as_ref()?
6101 .keep_selection_on_copy
6102 .as_ref()
6103 },
6104 write: |settings_content, value| {
6105 settings_content
6106 .terminal
6107 .get_or_insert_default()
6108 .keep_selection_on_copy = value;
6109 },
6110 }),
6111 metadata: None,
6112 files: USER,
6113 }),
6114 ]
6115 }
6116
6117 fn layout_settings_section() -> [SettingsPageItem; 3] {
6118 [
6119 SettingsPageItem::SectionHeader("Layout Settings"),
6120 SettingsPageItem::SettingItem(SettingItem {
6121 title: "Default Width",
6122 description: "Default width when the terminal is docked to the left or right (in pixels).",
6123 field: Box::new(SettingField {
6124 json_path: Some("terminal.default_width"),
6125 pick: |settings_content| {
6126 settings_content.terminal.as_ref()?.default_width.as_ref()
6127 },
6128 write: |settings_content, value| {
6129 settings_content
6130 .terminal
6131 .get_or_insert_default()
6132 .default_width = value;
6133 },
6134 }),
6135 metadata: None,
6136 files: USER,
6137 }),
6138 SettingsPageItem::SettingItem(SettingItem {
6139 title: "Default Height",
6140 description: "Default height when the terminal is docked to the bottom (in pixels).",
6141 field: Box::new(SettingField {
6142 json_path: Some("terminal.default_height"),
6143 pick: |settings_content| {
6144 settings_content.terminal.as_ref()?.default_height.as_ref()
6145 },
6146 write: |settings_content, value| {
6147 settings_content
6148 .terminal
6149 .get_or_insert_default()
6150 .default_height = value;
6151 },
6152 }),
6153 metadata: None,
6154 files: USER,
6155 }),
6156 ]
6157 }
6158
6159 fn advanced_settings_section() -> [SettingsPageItem; 3] {
6160 [
6161 SettingsPageItem::SectionHeader("Advanced Settings"),
6162 SettingsPageItem::SettingItem(SettingItem {
6163 title: "Max Scroll History Lines",
6164 description: "Maximum number of lines to keep in scrollback history (max: 100,000; 0 disables scrolling).",
6165 field: Box::new(SettingField {
6166 json_path: Some("terminal.max_scroll_history_lines"),
6167 pick: |settings_content| {
6168 settings_content
6169 .terminal
6170 .as_ref()?
6171 .max_scroll_history_lines
6172 .as_ref()
6173 },
6174 write: |settings_content, value| {
6175 settings_content
6176 .terminal
6177 .get_or_insert_default()
6178 .max_scroll_history_lines = value;
6179 },
6180 }),
6181 metadata: None,
6182 files: USER,
6183 }),
6184 SettingsPageItem::SettingItem(SettingItem {
6185 title: "Scroll Multiplier",
6186 description: "The multiplier for scrolling in the terminal with the mouse wheel",
6187 field: Box::new(SettingField {
6188 json_path: Some("terminal.scroll_multiplier"),
6189 pick: |settings_content| {
6190 settings_content
6191 .terminal
6192 .as_ref()?
6193 .scroll_multiplier
6194 .as_ref()
6195 },
6196 write: |settings_content, value| {
6197 settings_content
6198 .terminal
6199 .get_or_insert_default()
6200 .scroll_multiplier = value;
6201 },
6202 }),
6203 metadata: None,
6204 files: USER,
6205 }),
6206 ]
6207 }
6208
6209 fn toolbar_section() -> [SettingsPageItem; 2] {
6210 [
6211 SettingsPageItem::SectionHeader("Toolbar"),
6212 SettingsPageItem::SettingItem(SettingItem {
6213 title: "Breadcrumbs",
6214 description: "Display the terminal title in breadcrumbs inside the terminal pane.",
6215 field: Box::new(SettingField {
6216 json_path: Some("terminal.toolbar.breadcrumbs"),
6217 pick: |settings_content| {
6218 settings_content
6219 .terminal
6220 .as_ref()?
6221 .toolbar
6222 .as_ref()?
6223 .breadcrumbs
6224 .as_ref()
6225 },
6226 write: |settings_content, value| {
6227 settings_content
6228 .terminal
6229 .get_or_insert_default()
6230 .toolbar
6231 .get_or_insert_default()
6232 .breadcrumbs = value;
6233 },
6234 }),
6235 metadata: None,
6236 files: USER,
6237 }),
6238 ]
6239 }
6240
6241 fn scrollbar_section() -> [SettingsPageItem; 2] {
6242 [
6243 SettingsPageItem::SectionHeader("Scrollbar"),
6244 SettingsPageItem::SettingItem(SettingItem {
6245 title: "Show Scrollbar",
6246 description: "When to show the scrollbar in the terminal.",
6247 field: Box::new(SettingField {
6248 json_path: Some("terminal.scrollbar.show"),
6249 pick: |settings_content| {
6250 show_scrollbar_or_editor(settings_content, |settings_content| {
6251 settings_content
6252 .terminal
6253 .as_ref()?
6254 .scrollbar
6255 .as_ref()?
6256 .show
6257 .as_ref()
6258 })
6259 },
6260 write: |settings_content, value| {
6261 settings_content
6262 .terminal
6263 .get_or_insert_default()
6264 .scrollbar
6265 .get_or_insert_default()
6266 .show = value;
6267 },
6268 }),
6269 metadata: None,
6270 files: USER,
6271 }),
6272 ]
6273 }
6274
6275 SettingsPage {
6276 title: "Terminal",
6277 items: concat_sections![
6278 environment_section(),
6279 font_section(),
6280 display_settings_section(),
6281 behavior_settings_section(),
6282 layout_settings_section(),
6283 advanced_settings_section(),
6284 toolbar_section(),
6285 scrollbar_section(),
6286 ],
6287 }
6288}
6289
6290fn version_control_page() -> SettingsPage {
6291 fn git_integration_section() -> [SettingsPageItem; 2] {
6292 [
6293 SettingsPageItem::SectionHeader("Git Integration"),
6294 SettingsPageItem::DynamicItem(DynamicItem {
6295 discriminant: SettingItem {
6296 files: USER,
6297 title: "Disable Git Integration",
6298 description: "Disable all Git integration features in Zed.",
6299 field: Box::new(SettingField::<bool> {
6300 json_path: Some("git.disable_git"),
6301 pick: |settings_content| {
6302 settings_content
6303 .git
6304 .as_ref()?
6305 .enabled
6306 .as_ref()?
6307 .disable_git
6308 .as_ref()
6309 },
6310 write: |settings_content, value| {
6311 settings_content
6312 .git
6313 .get_or_insert_default()
6314 .enabled
6315 .get_or_insert_default()
6316 .disable_git = value;
6317 },
6318 }),
6319 metadata: None,
6320 },
6321 pick_discriminant: |settings_content| {
6322 let disabled = settings_content
6323 .git
6324 .as_ref()?
6325 .enabled
6326 .as_ref()?
6327 .disable_git
6328 .unwrap_or(false);
6329 Some(if disabled { 0 } else { 1 })
6330 },
6331 fields: vec![
6332 vec![],
6333 vec![
6334 SettingItem {
6335 files: USER,
6336 title: "Enable Git Status",
6337 description: "Show Git status information in the editor.",
6338 field: Box::new(SettingField::<bool> {
6339 json_path: Some("git.enable_status"),
6340 pick: |settings_content| {
6341 settings_content
6342 .git
6343 .as_ref()?
6344 .enabled
6345 .as_ref()?
6346 .enable_status
6347 .as_ref()
6348 },
6349 write: |settings_content, value| {
6350 settings_content
6351 .git
6352 .get_or_insert_default()
6353 .enabled
6354 .get_or_insert_default()
6355 .enable_status = value;
6356 },
6357 }),
6358 metadata: None,
6359 },
6360 SettingItem {
6361 files: USER,
6362 title: "Enable Git Diff",
6363 description: "Show Git diff information in the editor.",
6364 field: Box::new(SettingField::<bool> {
6365 json_path: Some("git.enable_diff"),
6366 pick: |settings_content| {
6367 settings_content
6368 .git
6369 .as_ref()?
6370 .enabled
6371 .as_ref()?
6372 .enable_diff
6373 .as_ref()
6374 },
6375 write: |settings_content, value| {
6376 settings_content
6377 .git
6378 .get_or_insert_default()
6379 .enabled
6380 .get_or_insert_default()
6381 .enable_diff = value;
6382 },
6383 }),
6384 metadata: None,
6385 },
6386 ],
6387 ],
6388 }),
6389 ]
6390 }
6391
6392 fn git_gutter_section() -> [SettingsPageItem; 3] {
6393 [
6394 SettingsPageItem::SectionHeader("Git Gutter"),
6395 SettingsPageItem::SettingItem(SettingItem {
6396 title: "Visibility",
6397 description: "Control whether Git status is shown in the editor's gutter.",
6398 field: Box::new(SettingField {
6399 json_path: Some("git.git_gutter"),
6400 pick: |settings_content| settings_content.git.as_ref()?.git_gutter.as_ref(),
6401 write: |settings_content, value| {
6402 settings_content.git.get_or_insert_default().git_gutter = value;
6403 },
6404 }),
6405 metadata: None,
6406 files: USER,
6407 }),
6408 // todo(settings_ui): Figure out the right default for this value in default.json
6409 SettingsPageItem::SettingItem(SettingItem {
6410 title: "Debounce",
6411 description: "Debounce threshold in milliseconds after which changes are reflected in the Git gutter.",
6412 field: Box::new(SettingField {
6413 json_path: Some("git.gutter_debounce"),
6414 pick: |settings_content| {
6415 settings_content.git.as_ref()?.gutter_debounce.as_ref()
6416 },
6417 write: |settings_content, value| {
6418 settings_content.git.get_or_insert_default().gutter_debounce = value;
6419 },
6420 }),
6421 metadata: None,
6422 files: USER,
6423 }),
6424 ]
6425 }
6426
6427 fn inline_git_blame_section() -> [SettingsPageItem; 6] {
6428 [
6429 SettingsPageItem::SectionHeader("Inline Git Blame"),
6430 SettingsPageItem::SettingItem(SettingItem {
6431 title: "Enabled",
6432 description: "Whether or not to show Git blame data inline in the currently focused line.",
6433 field: Box::new(SettingField {
6434 json_path: Some("git.inline_blame.enabled"),
6435 pick: |settings_content| {
6436 settings_content
6437 .git
6438 .as_ref()?
6439 .inline_blame
6440 .as_ref()?
6441 .enabled
6442 .as_ref()
6443 },
6444 write: |settings_content, value| {
6445 settings_content
6446 .git
6447 .get_or_insert_default()
6448 .inline_blame
6449 .get_or_insert_default()
6450 .enabled = value;
6451 },
6452 }),
6453 metadata: None,
6454 files: USER,
6455 }),
6456 SettingsPageItem::SettingItem(SettingItem {
6457 title: "Delay",
6458 description: "The delay after which the inline blame information is shown.",
6459 field: Box::new(SettingField {
6460 json_path: Some("git.inline_blame.delay_ms"),
6461 pick: |settings_content| {
6462 settings_content
6463 .git
6464 .as_ref()?
6465 .inline_blame
6466 .as_ref()?
6467 .delay_ms
6468 .as_ref()
6469 },
6470 write: |settings_content, value| {
6471 settings_content
6472 .git
6473 .get_or_insert_default()
6474 .inline_blame
6475 .get_or_insert_default()
6476 .delay_ms = value;
6477 },
6478 }),
6479 metadata: None,
6480 files: USER,
6481 }),
6482 SettingsPageItem::SettingItem(SettingItem {
6483 title: "Padding",
6484 description: "Padding between the end of the source line and the start of the inline blame in columns.",
6485 field: Box::new(SettingField {
6486 json_path: Some("git.inline_blame.padding"),
6487 pick: |settings_content| {
6488 settings_content
6489 .git
6490 .as_ref()?
6491 .inline_blame
6492 .as_ref()?
6493 .padding
6494 .as_ref()
6495 },
6496 write: |settings_content, value| {
6497 settings_content
6498 .git
6499 .get_or_insert_default()
6500 .inline_blame
6501 .get_or_insert_default()
6502 .padding = value;
6503 },
6504 }),
6505 metadata: None,
6506 files: USER,
6507 }),
6508 SettingsPageItem::SettingItem(SettingItem {
6509 title: "Minimum Column",
6510 description: "The minimum column number at which to show the inline blame information.",
6511 field: Box::new(SettingField {
6512 json_path: Some("git.inline_blame.min_column"),
6513 pick: |settings_content| {
6514 settings_content
6515 .git
6516 .as_ref()?
6517 .inline_blame
6518 .as_ref()?
6519 .min_column
6520 .as_ref()
6521 },
6522 write: |settings_content, value| {
6523 settings_content
6524 .git
6525 .get_or_insert_default()
6526 .inline_blame
6527 .get_or_insert_default()
6528 .min_column = value;
6529 },
6530 }),
6531 metadata: None,
6532 files: USER,
6533 }),
6534 SettingsPageItem::SettingItem(SettingItem {
6535 title: "Show Commit Summary",
6536 description: "Show commit summary as part of the inline blame.",
6537 field: Box::new(SettingField {
6538 json_path: Some("git.inline_blame.show_commit_summary"),
6539 pick: |settings_content| {
6540 settings_content
6541 .git
6542 .as_ref()?
6543 .inline_blame
6544 .as_ref()?
6545 .show_commit_summary
6546 .as_ref()
6547 },
6548 write: |settings_content, value| {
6549 settings_content
6550 .git
6551 .get_or_insert_default()
6552 .inline_blame
6553 .get_or_insert_default()
6554 .show_commit_summary = value;
6555 },
6556 }),
6557 metadata: None,
6558 files: USER,
6559 }),
6560 ]
6561 }
6562
6563 fn git_blame_view_section() -> [SettingsPageItem; 2] {
6564 [
6565 SettingsPageItem::SectionHeader("Git Blame View"),
6566 SettingsPageItem::SettingItem(SettingItem {
6567 title: "Show Avatar",
6568 description: "Show the avatar of the author of the commit.",
6569 field: Box::new(SettingField {
6570 json_path: Some("git.blame.show_avatar"),
6571 pick: |settings_content| {
6572 settings_content
6573 .git
6574 .as_ref()?
6575 .blame
6576 .as_ref()?
6577 .show_avatar
6578 .as_ref()
6579 },
6580 write: |settings_content, value| {
6581 settings_content
6582 .git
6583 .get_or_insert_default()
6584 .blame
6585 .get_or_insert_default()
6586 .show_avatar = value;
6587 },
6588 }),
6589 metadata: None,
6590 files: USER,
6591 }),
6592 ]
6593 }
6594
6595 fn branch_picker_section() -> [SettingsPageItem; 2] {
6596 [
6597 SettingsPageItem::SectionHeader("Branch Picker"),
6598 SettingsPageItem::SettingItem(SettingItem {
6599 title: "Show Author Name",
6600 description: "Show author name as part of the commit information in branch picker.",
6601 field: Box::new(SettingField {
6602 json_path: Some("git.branch_picker.show_author_name"),
6603 pick: |settings_content| {
6604 settings_content
6605 .git
6606 .as_ref()?
6607 .branch_picker
6608 .as_ref()?
6609 .show_author_name
6610 .as_ref()
6611 },
6612 write: |settings_content, value| {
6613 settings_content
6614 .git
6615 .get_or_insert_default()
6616 .branch_picker
6617 .get_or_insert_default()
6618 .show_author_name = value;
6619 },
6620 }),
6621 metadata: None,
6622 files: USER,
6623 }),
6624 ]
6625 }
6626
6627 fn git_hunks_section() -> [SettingsPageItem; 3] {
6628 [
6629 SettingsPageItem::SectionHeader("Git Hunks"),
6630 SettingsPageItem::SettingItem(SettingItem {
6631 title: "Hunk Style",
6632 description: "How Git hunks are displayed visually in the editor.",
6633 field: Box::new(SettingField {
6634 json_path: Some("git.hunk_style"),
6635 pick: |settings_content| settings_content.git.as_ref()?.hunk_style.as_ref(),
6636 write: |settings_content, value| {
6637 settings_content.git.get_or_insert_default().hunk_style = value;
6638 },
6639 }),
6640 metadata: None,
6641 files: USER,
6642 }),
6643 SettingsPageItem::SettingItem(SettingItem {
6644 title: "Path Style",
6645 description: "Should the name or path be displayed first in the git view.",
6646 field: Box::new(SettingField {
6647 json_path: Some("git.path_style"),
6648 pick: |settings_content| settings_content.git.as_ref()?.path_style.as_ref(),
6649 write: |settings_content, value| {
6650 settings_content.git.get_or_insert_default().path_style = value;
6651 },
6652 }),
6653 metadata: None,
6654 files: USER,
6655 }),
6656 ]
6657 }
6658
6659 SettingsPage {
6660 title: "Version Control",
6661 items: concat_sections![
6662 git_integration_section(),
6663 git_gutter_section(),
6664 inline_git_blame_section(),
6665 git_blame_view_section(),
6666 branch_picker_section(),
6667 git_hunks_section(),
6668 ],
6669 }
6670}
6671
6672fn collaboration_page() -> SettingsPage {
6673 fn calls_section() -> [SettingsPageItem; 3] {
6674 [
6675 SettingsPageItem::SectionHeader("Calls"),
6676 SettingsPageItem::SettingItem(SettingItem {
6677 title: "Mute On Join",
6678 description: "Whether the microphone should be muted when joining a channel or a call.",
6679 field: Box::new(SettingField {
6680 json_path: Some("calls.mute_on_join"),
6681 pick: |settings_content| settings_content.calls.as_ref()?.mute_on_join.as_ref(),
6682 write: |settings_content, value| {
6683 settings_content.calls.get_or_insert_default().mute_on_join = value;
6684 },
6685 }),
6686 metadata: None,
6687 files: USER,
6688 }),
6689 SettingsPageItem::SettingItem(SettingItem {
6690 title: "Share On Join",
6691 description: "Whether your current project should be shared when joining an empty channel.",
6692 field: Box::new(SettingField {
6693 json_path: Some("calls.share_on_join"),
6694 pick: |settings_content| {
6695 settings_content.calls.as_ref()?.share_on_join.as_ref()
6696 },
6697 write: |settings_content, value| {
6698 settings_content.calls.get_or_insert_default().share_on_join = value;
6699 },
6700 }),
6701 metadata: None,
6702 files: USER,
6703 }),
6704 ]
6705 }
6706
6707 fn experimental_section() -> [SettingsPageItem; 6] {
6708 [
6709 SettingsPageItem::SectionHeader("Experimental"),
6710 SettingsPageItem::SettingItem(SettingItem {
6711 title: "Rodio Audio",
6712 description: "Opt into the new audio system.",
6713 field: Box::new(SettingField {
6714 json_path: Some("audio.experimental.rodio_audio"),
6715 pick: |settings_content| settings_content.audio.as_ref()?.rodio_audio.as_ref(),
6716 write: |settings_content, value| {
6717 settings_content.audio.get_or_insert_default().rodio_audio = value;
6718 },
6719 }),
6720 metadata: None,
6721 files: USER,
6722 }),
6723 SettingsPageItem::SettingItem(SettingItem {
6724 title: "Auto Microphone Volume",
6725 description: "Automatically adjust microphone volume (requires rodio audio).",
6726 field: Box::new(SettingField {
6727 json_path: Some("audio.experimental.auto_microphone_volume"),
6728 pick: |settings_content| {
6729 settings_content
6730 .audio
6731 .as_ref()?
6732 .auto_microphone_volume
6733 .as_ref()
6734 },
6735 write: |settings_content, value| {
6736 settings_content
6737 .audio
6738 .get_or_insert_default()
6739 .auto_microphone_volume = value;
6740 },
6741 }),
6742 metadata: None,
6743 files: USER,
6744 }),
6745 SettingsPageItem::SettingItem(SettingItem {
6746 title: "Auto Speaker Volume",
6747 description: "Automatically adjust volume of other call members (requires rodio audio).",
6748 field: Box::new(SettingField {
6749 json_path: Some("audio.experimental.auto_speaker_volume"),
6750 pick: |settings_content| {
6751 settings_content
6752 .audio
6753 .as_ref()?
6754 .auto_speaker_volume
6755 .as_ref()
6756 },
6757 write: |settings_content, value| {
6758 settings_content
6759 .audio
6760 .get_or_insert_default()
6761 .auto_speaker_volume = value;
6762 },
6763 }),
6764 metadata: None,
6765 files: USER,
6766 }),
6767 SettingsPageItem::SettingItem(SettingItem {
6768 title: "Denoise",
6769 description: "Remove background noises (requires rodio audio).",
6770 field: Box::new(SettingField {
6771 json_path: Some("audio.experimental.denoise"),
6772 pick: |settings_content| settings_content.audio.as_ref()?.denoise.as_ref(),
6773 write: |settings_content, value| {
6774 settings_content.audio.get_or_insert_default().denoise = value;
6775 },
6776 }),
6777 metadata: None,
6778 files: USER,
6779 }),
6780 SettingsPageItem::SettingItem(SettingItem {
6781 title: "Legacy Audio Compatible",
6782 description: "Use audio parameters compatible with previous versions (requires rodio audio).",
6783 field: Box::new(SettingField {
6784 json_path: Some("audio.experimental.legacy_audio_compatible"),
6785 pick: |settings_content| {
6786 settings_content
6787 .audio
6788 .as_ref()?
6789 .legacy_audio_compatible
6790 .as_ref()
6791 },
6792 write: |settings_content, value| {
6793 settings_content
6794 .audio
6795 .get_or_insert_default()
6796 .legacy_audio_compatible = value;
6797 },
6798 }),
6799 metadata: None,
6800 files: USER,
6801 }),
6802 ]
6803 }
6804
6805 SettingsPage {
6806 title: "Collaboration",
6807 items: concat_sections![calls_section(), experimental_section()],
6808 }
6809}
6810
6811fn ai_page() -> SettingsPage {
6812 fn general_section() -> [SettingsPageItem; 2] {
6813 [
6814 SettingsPageItem::SectionHeader("General"),
6815 SettingsPageItem::SettingItem(SettingItem {
6816 title: "Disable AI",
6817 description: "Whether to disable all AI features in Zed.",
6818 field: Box::new(SettingField {
6819 json_path: Some("disable_ai"),
6820 pick: |settings_content| settings_content.disable_ai.as_ref(),
6821 write: |settings_content, value| {
6822 settings_content.disable_ai = value;
6823 },
6824 }),
6825 metadata: None,
6826 files: USER,
6827 }),
6828 ]
6829 }
6830
6831 fn agent_configuration_section() -> [SettingsPageItem; 11] {
6832 [
6833 SettingsPageItem::SectionHeader("Agent Configuration"),
6834 SettingsPageItem::SettingItem(SettingItem {
6835 title: "Always Allow Tool Actions",
6836 description: "When enabled, the agent can run potentially destructive actions without asking for your confirmation. This setting has no effect on external agents.",
6837 field: Box::new(SettingField {
6838 json_path: Some("agent.always_allow_tool_actions"),
6839 pick: |settings_content| {
6840 settings_content
6841 .agent
6842 .as_ref()?
6843 .always_allow_tool_actions
6844 .as_ref()
6845 },
6846 write: |settings_content, value| {
6847 settings_content
6848 .agent
6849 .get_or_insert_default()
6850 .always_allow_tool_actions = value;
6851 },
6852 }),
6853 metadata: None,
6854 files: USER,
6855 }),
6856 SettingsPageItem::SettingItem(SettingItem {
6857 title: "Single File Review",
6858 description: "When enabled, agent edits will also be displayed in single-file buffers for review.",
6859 field: Box::new(SettingField {
6860 json_path: Some("agent.single_file_review"),
6861 pick: |settings_content| {
6862 settings_content.agent.as_ref()?.single_file_review.as_ref()
6863 },
6864 write: |settings_content, value| {
6865 settings_content
6866 .agent
6867 .get_or_insert_default()
6868 .single_file_review = value;
6869 },
6870 }),
6871 metadata: None,
6872 files: USER,
6873 }),
6874 SettingsPageItem::SettingItem(SettingItem {
6875 title: "Enable Feedback",
6876 description: "Show voting thumbs up/down icon buttons for feedback on agent edits.",
6877 field: Box::new(SettingField {
6878 json_path: Some("agent.enable_feedback"),
6879 pick: |settings_content| {
6880 settings_content.agent.as_ref()?.enable_feedback.as_ref()
6881 },
6882 write: |settings_content, value| {
6883 settings_content
6884 .agent
6885 .get_or_insert_default()
6886 .enable_feedback = value;
6887 },
6888 }),
6889 metadata: None,
6890 files: USER,
6891 }),
6892 SettingsPageItem::SettingItem(SettingItem {
6893 title: "Notify When Agent Waiting",
6894 description: "Where to show notifications when the agent has completed its response or needs confirmation before running a tool action.",
6895 field: Box::new(SettingField {
6896 json_path: Some("agent.notify_when_agent_waiting"),
6897 pick: |settings_content| {
6898 settings_content
6899 .agent
6900 .as_ref()?
6901 .notify_when_agent_waiting
6902 .as_ref()
6903 },
6904 write: |settings_content, value| {
6905 settings_content
6906 .agent
6907 .get_or_insert_default()
6908 .notify_when_agent_waiting = value;
6909 },
6910 }),
6911 metadata: None,
6912 files: USER,
6913 }),
6914 SettingsPageItem::SettingItem(SettingItem {
6915 title: "Play Sound When Agent Done",
6916 description: "Whether to play a sound when the agent has either completed its response, or needs user input.",
6917 field: Box::new(SettingField {
6918 json_path: Some("agent.play_sound_when_agent_done"),
6919 pick: |settings_content| {
6920 settings_content
6921 .agent
6922 .as_ref()?
6923 .play_sound_when_agent_done
6924 .as_ref()
6925 },
6926 write: |settings_content, value| {
6927 settings_content
6928 .agent
6929 .get_or_insert_default()
6930 .play_sound_when_agent_done = value;
6931 },
6932 }),
6933 metadata: None,
6934 files: USER,
6935 }),
6936 SettingsPageItem::SettingItem(SettingItem {
6937 title: "Expand Edit Card",
6938 description: "Whether to have edit cards in the agent panel expanded, showing a Preview of the diff.",
6939 field: Box::new(SettingField {
6940 json_path: Some("agent.expand_edit_card"),
6941 pick: |settings_content| {
6942 settings_content.agent.as_ref()?.expand_edit_card.as_ref()
6943 },
6944 write: |settings_content, value| {
6945 settings_content
6946 .agent
6947 .get_or_insert_default()
6948 .expand_edit_card = value;
6949 },
6950 }),
6951 metadata: None,
6952 files: USER,
6953 }),
6954 SettingsPageItem::SettingItem(SettingItem {
6955 title: "Expand Terminal Card",
6956 description: "Whether to have terminal cards in the agent panel expanded, showing the whole command output.",
6957 field: Box::new(SettingField {
6958 json_path: Some("agent.expand_terminal_card"),
6959 pick: |settings_content| {
6960 settings_content
6961 .agent
6962 .as_ref()?
6963 .expand_terminal_card
6964 .as_ref()
6965 },
6966 write: |settings_content, value| {
6967 settings_content
6968 .agent
6969 .get_or_insert_default()
6970 .expand_terminal_card = value;
6971 },
6972 }),
6973 metadata: None,
6974 files: USER,
6975 }),
6976 SettingsPageItem::SettingItem(SettingItem {
6977 title: "Use Modifier To Send",
6978 description: "Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages.",
6979 field: Box::new(SettingField {
6980 json_path: Some("agent.use_modifier_to_send"),
6981 pick: |settings_content| {
6982 settings_content
6983 .agent
6984 .as_ref()?
6985 .use_modifier_to_send
6986 .as_ref()
6987 },
6988 write: |settings_content, value| {
6989 settings_content
6990 .agent
6991 .get_or_insert_default()
6992 .use_modifier_to_send = value;
6993 },
6994 }),
6995 metadata: None,
6996 files: USER,
6997 }),
6998 SettingsPageItem::SettingItem(SettingItem {
6999 title: "Message Editor Min Lines",
7000 description: "Minimum number of lines to display in the agent message editor.",
7001 field: Box::new(SettingField {
7002 json_path: Some("agent.message_editor_min_lines"),
7003 pick: |settings_content| {
7004 settings_content
7005 .agent
7006 .as_ref()?
7007 .message_editor_min_lines
7008 .as_ref()
7009 },
7010 write: |settings_content, value| {
7011 settings_content
7012 .agent
7013 .get_or_insert_default()
7014 .message_editor_min_lines = value;
7015 },
7016 }),
7017 metadata: None,
7018 files: USER,
7019 }),
7020 SettingsPageItem::SettingItem(SettingItem {
7021 title: "Show Turn Stats",
7022 description: "Whether to show turn statistics like elapsed time during generation and final turn duration.",
7023 field: Box::new(SettingField {
7024 json_path: Some("agent.show_turn_stats"),
7025 pick: |settings_content| {
7026 settings_content.agent.as_ref()?.show_turn_stats.as_ref()
7027 },
7028 write: |settings_content, value| {
7029 settings_content
7030 .agent
7031 .get_or_insert_default()
7032 .show_turn_stats = value;
7033 },
7034 }),
7035 metadata: None,
7036 files: USER,
7037 }),
7038 ]
7039 }
7040
7041 fn context_servers_section() -> [SettingsPageItem; 2] {
7042 [
7043 SettingsPageItem::SectionHeader("Context Servers"),
7044 SettingsPageItem::SettingItem(SettingItem {
7045 title: "Context Server Timeout",
7046 description: "Default timeout in seconds for context server tool calls. Can be overridden per-server in context_servers configuration.",
7047 field: Box::new(SettingField {
7048 json_path: Some("context_server_timeout"),
7049 pick: |settings_content| {
7050 settings_content.project.context_server_timeout.as_ref()
7051 },
7052 write: |settings_content, value| {
7053 settings_content.project.context_server_timeout = value;
7054 },
7055 }),
7056 metadata: None,
7057 files: USER | PROJECT,
7058 }),
7059 ]
7060 }
7061
7062 fn edit_prediction_display_sub_section() -> [SettingsPageItem; 2] {
7063 [
7064 SettingsPageItem::SettingItem(SettingItem {
7065 title: "Display Mode",
7066 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.",
7067 field: Box::new(SettingField {
7068 json_path: Some("edit_prediction.display_mode"),
7069 pick: |settings_content| {
7070 settings_content
7071 .project
7072 .all_languages
7073 .edit_predictions
7074 .as_ref()?
7075 .mode
7076 .as_ref()
7077 },
7078 write: |settings_content, value| {
7079 settings_content
7080 .project
7081 .all_languages
7082 .edit_predictions
7083 .get_or_insert_default()
7084 .mode = value;
7085 },
7086 }),
7087 metadata: None,
7088 files: USER,
7089 }),
7090 SettingsPageItem::SettingItem(SettingItem {
7091 title: "Display In Text Threads",
7092 description: "Whether edit predictions are enabled when editing text threads in the agent panel.",
7093 field: Box::new(SettingField {
7094 json_path: Some("edit_prediction.in_text_threads"),
7095 pick: |settings_content| {
7096 settings_content
7097 .project
7098 .all_languages
7099 .edit_predictions
7100 .as_ref()?
7101 .enabled_in_text_threads
7102 .as_ref()
7103 },
7104 write: |settings_content, value| {
7105 settings_content
7106 .project
7107 .all_languages
7108 .edit_predictions
7109 .get_or_insert_default()
7110 .enabled_in_text_threads = value;
7111 },
7112 }),
7113 metadata: None,
7114 files: USER,
7115 }),
7116 ]
7117 }
7118
7119 SettingsPage {
7120 title: "AI",
7121 items: concat_sections![
7122 general_section(),
7123 agent_configuration_section(),
7124 context_servers_section(),
7125 edit_prediction_language_settings_section(),
7126 edit_prediction_display_sub_section()
7127 ],
7128 }
7129}
7130
7131fn network_page() -> SettingsPage {
7132 fn network_section() -> [SettingsPageItem; 3] {
7133 [
7134 SettingsPageItem::SectionHeader("Network"),
7135 SettingsPageItem::SettingItem(SettingItem {
7136 title: "Proxy",
7137 description: "The proxy to use for network requests.",
7138 field: Box::new(SettingField {
7139 json_path: Some("proxy"),
7140 pick: |settings_content| settings_content.proxy.as_ref(),
7141 write: |settings_content, value| {
7142 settings_content.proxy = value;
7143 },
7144 }),
7145 metadata: Some(Box::new(SettingsFieldMetadata {
7146 placeholder: Some("socks5h://localhost:10808"),
7147 ..Default::default()
7148 })),
7149 files: USER,
7150 }),
7151 SettingsPageItem::SettingItem(SettingItem {
7152 title: "Server URL",
7153 description: "The URL of the Zed server to connect to.",
7154 field: Box::new(SettingField {
7155 json_path: Some("server_url"),
7156 pick: |settings_content| settings_content.server_url.as_ref(),
7157 write: |settings_content, value| {
7158 settings_content.server_url = value;
7159 },
7160 }),
7161 metadata: Some(Box::new(SettingsFieldMetadata {
7162 placeholder: Some("https://zed.dev"),
7163 ..Default::default()
7164 })),
7165 files: USER,
7166 }),
7167 ]
7168 }
7169
7170 SettingsPage {
7171 title: "Network",
7172 items: concat_sections![network_section()],
7173 }
7174}
7175
7176const LANGUAGES_SECTION_HEADER: &'static str = "Languages";
7177
7178fn current_language() -> Option<SharedString> {
7179 sub_page_stack().iter().find_map(|page| {
7180 (page.section_header == LANGUAGES_SECTION_HEADER).then(|| page.link.title.clone())
7181 })
7182}
7183
7184fn language_settings_field<T>(
7185 settings_content: &SettingsContent,
7186 get: fn(&LanguageSettingsContent) -> Option<&T>,
7187) -> Option<&T> {
7188 let all_languages = &settings_content.project.all_languages;
7189 if let Some(current_language_name) = current_language() {
7190 if let Some(current_language) = all_languages.languages.0.get(¤t_language_name) {
7191 let value = get(current_language);
7192 if value.is_some() {
7193 return value;
7194 }
7195 }
7196 }
7197 let default_value = get(&all_languages.defaults);
7198 return default_value;
7199}
7200
7201fn language_settings_field_mut<T>(
7202 settings_content: &mut SettingsContent,
7203 value: Option<T>,
7204 write: fn(&mut LanguageSettingsContent, Option<T>),
7205) {
7206 let all_languages = &mut settings_content.project.all_languages;
7207 let language_content = if let Some(current_language) = current_language() {
7208 all_languages
7209 .languages
7210 .0
7211 .entry(current_language)
7212 .or_default()
7213 } else {
7214 &mut all_languages.defaults
7215 };
7216 write(language_content, value);
7217}
7218
7219fn language_settings_data() -> Box<[SettingsPageItem]> {
7220 fn indentation_section() -> [SettingsPageItem; 5] {
7221 [
7222 SettingsPageItem::SectionHeader("Indentation"),
7223 SettingsPageItem::SettingItem(SettingItem {
7224 title: "Tab Size",
7225 description: "How many columns a tab should occupy.",
7226 field: Box::new(SettingField {
7227 json_path: Some("languages.$(language).tab_size"), // TODO(cameron): not JQ syntax because not URL-safe
7228 pick: |settings_content| {
7229 language_settings_field(settings_content, |language| {
7230 language.tab_size.as_ref()
7231 })
7232 },
7233 write: |settings_content, value| {
7234 language_settings_field_mut(settings_content, value, |language, value| {
7235 language.tab_size = value;
7236 })
7237 },
7238 }),
7239 metadata: None,
7240 files: USER | PROJECT,
7241 }),
7242 SettingsPageItem::SettingItem(SettingItem {
7243 title: "Hard Tabs",
7244 description: "Whether to indent lines using tab characters, as opposed to multiple spaces.",
7245 field: Box::new(SettingField {
7246 json_path: Some("languages.$(language).hard_tabs"),
7247 pick: |settings_content| {
7248 language_settings_field(settings_content, |language| {
7249 language.hard_tabs.as_ref()
7250 })
7251 },
7252 write: |settings_content, value| {
7253 language_settings_field_mut(settings_content, value, |language, value| {
7254 language.hard_tabs = value;
7255 })
7256 },
7257 }),
7258 metadata: None,
7259 files: USER | PROJECT,
7260 }),
7261 SettingsPageItem::SettingItem(SettingItem {
7262 title: "Auto Indent",
7263 description: "Whether indentation should be adjusted based on the context whilst typing.",
7264 field: Box::new(SettingField {
7265 json_path: Some("languages.$(language).auto_indent"),
7266 pick: |settings_content| {
7267 language_settings_field(settings_content, |language| {
7268 language.auto_indent.as_ref()
7269 })
7270 },
7271 write: |settings_content, value| {
7272 language_settings_field_mut(settings_content, value, |language, value| {
7273 language.auto_indent = value;
7274 })
7275 },
7276 }),
7277 metadata: None,
7278 files: USER | PROJECT,
7279 }),
7280 SettingsPageItem::SettingItem(SettingItem {
7281 title: "Auto Indent On Paste",
7282 description: "Whether indentation of pasted content should be adjusted based on the context.",
7283 field: Box::new(SettingField {
7284 json_path: Some("languages.$(language).auto_indent_on_paste"),
7285 pick: |settings_content| {
7286 language_settings_field(settings_content, |language| {
7287 language.auto_indent_on_paste.as_ref()
7288 })
7289 },
7290 write: |settings_content, value| {
7291 language_settings_field_mut(settings_content, value, |language, value| {
7292 language.auto_indent_on_paste = value;
7293 })
7294 },
7295 }),
7296 metadata: None,
7297 files: USER | PROJECT,
7298 }),
7299 ]
7300 }
7301
7302 fn wrapping_section() -> [SettingsPageItem; 6] {
7303 [
7304 SettingsPageItem::SectionHeader("Wrapping"),
7305 SettingsPageItem::SettingItem(SettingItem {
7306 title: "Soft Wrap",
7307 description: "How to soft-wrap long lines of text.",
7308 field: Box::new(SettingField {
7309 json_path: Some("languages.$(language).soft_wrap"),
7310 pick: |settings_content| {
7311 language_settings_field(settings_content, |language| {
7312 language.soft_wrap.as_ref()
7313 })
7314 },
7315 write: |settings_content, value| {
7316 language_settings_field_mut(settings_content, value, |language, value| {
7317 language.soft_wrap = value;
7318 })
7319 },
7320 }),
7321 metadata: None,
7322 files: USER | PROJECT,
7323 }),
7324 SettingsPageItem::SettingItem(SettingItem {
7325 title: "Show Wrap Guides",
7326 description: "Show wrap guides in the editor.",
7327 field: Box::new(SettingField {
7328 json_path: Some("languages.$(language).show_wrap_guides"),
7329 pick: |settings_content| {
7330 language_settings_field(settings_content, |language| {
7331 language.show_wrap_guides.as_ref()
7332 })
7333 },
7334 write: |settings_content, value| {
7335 language_settings_field_mut(settings_content, value, |language, value| {
7336 language.show_wrap_guides = value;
7337 })
7338 },
7339 }),
7340 metadata: None,
7341 files: USER | PROJECT,
7342 }),
7343 SettingsPageItem::SettingItem(SettingItem {
7344 title: "Preferred Line Length",
7345 description: "The column at which to soft-wrap lines, for buffers where soft-wrap is enabled.",
7346 field: Box::new(SettingField {
7347 json_path: Some("languages.$(language).preferred_line_length"),
7348 pick: |settings_content| {
7349 language_settings_field(settings_content, |language| {
7350 language.preferred_line_length.as_ref()
7351 })
7352 },
7353 write: |settings_content, value| {
7354 language_settings_field_mut(settings_content, value, |language, value| {
7355 language.preferred_line_length = value;
7356 })
7357 },
7358 }),
7359 metadata: None,
7360 files: USER | PROJECT,
7361 }),
7362 SettingsPageItem::SettingItem(SettingItem {
7363 title: "Wrap Guides",
7364 description: "Character counts at which to show wrap guides in the editor.",
7365 field: Box::new(
7366 SettingField {
7367 json_path: Some("languages.$(language).wrap_guides"),
7368 pick: |settings_content| {
7369 language_settings_field(settings_content, |language| {
7370 language.wrap_guides.as_ref()
7371 })
7372 },
7373 write: |settings_content, value| {
7374 language_settings_field_mut(
7375 settings_content,
7376 value,
7377 |language, value| {
7378 language.wrap_guides = value;
7379 },
7380 )
7381 },
7382 }
7383 .unimplemented(),
7384 ),
7385 metadata: None,
7386 files: USER | PROJECT,
7387 }),
7388 SettingsPageItem::SettingItem(SettingItem {
7389 title: "Allow Rewrap",
7390 description: "Controls where the `editor::rewrap` action is allowed for this language.",
7391 field: Box::new(SettingField {
7392 json_path: Some("languages.$(language).allow_rewrap"),
7393 pick: |settings_content| {
7394 language_settings_field(settings_content, |language| {
7395 language.allow_rewrap.as_ref()
7396 })
7397 },
7398 write: |settings_content, value| {
7399 language_settings_field_mut(settings_content, value, |language, value| {
7400 language.allow_rewrap = value;
7401 })
7402 },
7403 }),
7404 metadata: None,
7405 files: USER | PROJECT,
7406 }),
7407 ]
7408 }
7409
7410 fn indent_guides_section() -> [SettingsPageItem; 6] {
7411 [
7412 SettingsPageItem::SectionHeader("Indent Guides"),
7413 SettingsPageItem::SettingItem(SettingItem {
7414 title: "Enabled",
7415 description: "Display indent guides in the editor.",
7416 field: Box::new(SettingField {
7417 json_path: Some("languages.$(language).indent_guides.enabled"),
7418 pick: |settings_content| {
7419 language_settings_field(settings_content, |language| {
7420 language
7421 .indent_guides
7422 .as_ref()
7423 .and_then(|indent_guides| indent_guides.enabled.as_ref())
7424 })
7425 },
7426 write: |settings_content, value| {
7427 language_settings_field_mut(settings_content, value, |language, value| {
7428 language.indent_guides.get_or_insert_default().enabled = value;
7429 })
7430 },
7431 }),
7432 metadata: None,
7433 files: USER | PROJECT,
7434 }),
7435 SettingsPageItem::SettingItem(SettingItem {
7436 title: "Line Width",
7437 description: "The width of the indent guides in pixels, between 1 and 10.",
7438 field: Box::new(SettingField {
7439 json_path: Some("languages.$(language).indent_guides.line_width"),
7440 pick: |settings_content| {
7441 language_settings_field(settings_content, |language| {
7442 language
7443 .indent_guides
7444 .as_ref()
7445 .and_then(|indent_guides| indent_guides.line_width.as_ref())
7446 })
7447 },
7448 write: |settings_content, value| {
7449 language_settings_field_mut(settings_content, value, |language, value| {
7450 language.indent_guides.get_or_insert_default().line_width = value;
7451 })
7452 },
7453 }),
7454 metadata: None,
7455 files: USER | PROJECT,
7456 }),
7457 SettingsPageItem::SettingItem(SettingItem {
7458 title: "Active Line Width",
7459 description: "The width of the active indent guide in pixels, between 1 and 10.",
7460 field: Box::new(SettingField {
7461 json_path: Some("languages.$(language).indent_guides.active_line_width"),
7462 pick: |settings_content| {
7463 language_settings_field(settings_content, |language| {
7464 language
7465 .indent_guides
7466 .as_ref()
7467 .and_then(|indent_guides| indent_guides.active_line_width.as_ref())
7468 })
7469 },
7470 write: |settings_content, value| {
7471 language_settings_field_mut(settings_content, value, |language, value| {
7472 language
7473 .indent_guides
7474 .get_or_insert_default()
7475 .active_line_width = value;
7476 })
7477 },
7478 }),
7479 metadata: None,
7480 files: USER | PROJECT,
7481 }),
7482 SettingsPageItem::SettingItem(SettingItem {
7483 title: "Coloring",
7484 description: "Determines how indent guides are colored.",
7485 field: Box::new(SettingField {
7486 json_path: Some("languages.$(language).indent_guides.coloring"),
7487 pick: |settings_content| {
7488 language_settings_field(settings_content, |language| {
7489 language
7490 .indent_guides
7491 .as_ref()
7492 .and_then(|indent_guides| indent_guides.coloring.as_ref())
7493 })
7494 },
7495 write: |settings_content, value| {
7496 language_settings_field_mut(settings_content, value, |language, value| {
7497 language.indent_guides.get_or_insert_default().coloring = value;
7498 })
7499 },
7500 }),
7501 metadata: None,
7502 files: USER | PROJECT,
7503 }),
7504 SettingsPageItem::SettingItem(SettingItem {
7505 title: "Background Coloring",
7506 description: "Determines how indent guide backgrounds are colored.",
7507 field: Box::new(SettingField {
7508 json_path: Some("languages.$(language).indent_guides.background_coloring"),
7509 pick: |settings_content| {
7510 language_settings_field(settings_content, |language| {
7511 language.indent_guides.as_ref().and_then(|indent_guides| {
7512 indent_guides.background_coloring.as_ref()
7513 })
7514 })
7515 },
7516 write: |settings_content, value| {
7517 language_settings_field_mut(settings_content, value, |language, value| {
7518 language
7519 .indent_guides
7520 .get_or_insert_default()
7521 .background_coloring = value;
7522 })
7523 },
7524 }),
7525 metadata: None,
7526 files: USER | PROJECT,
7527 }),
7528 ]
7529 }
7530
7531 fn formatting_section() -> [SettingsPageItem; 7] {
7532 [
7533 SettingsPageItem::SectionHeader("Formatting"),
7534 SettingsPageItem::SettingItem(SettingItem {
7535 title: "Format On Save",
7536 description: "Whether or not to perform a buffer format before saving.",
7537 field: Box::new(
7538 // TODO(settings_ui): this setting should just be a bool
7539 SettingField {
7540 json_path: Some("languages.$(language).format_on_save"),
7541 pick: |settings_content| {
7542 language_settings_field(settings_content, |language| {
7543 language.format_on_save.as_ref()
7544 })
7545 },
7546 write: |settings_content, value| {
7547 language_settings_field_mut(
7548 settings_content,
7549 value,
7550 |language, value| {
7551 language.format_on_save = value;
7552 },
7553 )
7554 },
7555 },
7556 ),
7557 metadata: None,
7558 files: USER | PROJECT,
7559 }),
7560 SettingsPageItem::SettingItem(SettingItem {
7561 title: "Remove Trailing Whitespace On Save",
7562 description: "Whether or not to remove any trailing whitespace from lines of a buffer before saving it.",
7563 field: Box::new(SettingField {
7564 json_path: Some("languages.$(language).remove_trailing_whitespace_on_save"),
7565 pick: |settings_content| {
7566 language_settings_field(settings_content, |language| {
7567 language.remove_trailing_whitespace_on_save.as_ref()
7568 })
7569 },
7570 write: |settings_content, value| {
7571 language_settings_field_mut(settings_content, value, |language, value| {
7572 language.remove_trailing_whitespace_on_save = value;
7573 })
7574 },
7575 }),
7576 metadata: None,
7577 files: USER | PROJECT,
7578 }),
7579 SettingsPageItem::SettingItem(SettingItem {
7580 title: "Ensure Final Newline On Save",
7581 description: "Whether or not to ensure there's a single newline at the end of a buffer when saving it.",
7582 field: Box::new(SettingField {
7583 json_path: Some("languages.$(language).ensure_final_newline_on_save"),
7584 pick: |settings_content| {
7585 language_settings_field(settings_content, |language| {
7586 language.ensure_final_newline_on_save.as_ref()
7587 })
7588 },
7589 write: |settings_content, value| {
7590 language_settings_field_mut(settings_content, value, |language, value| {
7591 language.ensure_final_newline_on_save = value;
7592 })
7593 },
7594 }),
7595 metadata: None,
7596 files: USER | PROJECT,
7597 }),
7598 SettingsPageItem::SettingItem(SettingItem {
7599 title: "Formatter",
7600 description: "How to perform a buffer format.",
7601 field: Box::new(
7602 SettingField {
7603 json_path: Some("languages.$(language).formatter"),
7604 pick: |settings_content| {
7605 language_settings_field(settings_content, |language| {
7606 language.formatter.as_ref()
7607 })
7608 },
7609 write: |settings_content, value| {
7610 language_settings_field_mut(
7611 settings_content,
7612 value,
7613 |language, value| {
7614 language.formatter = value;
7615 },
7616 )
7617 },
7618 }
7619 .unimplemented(),
7620 ),
7621 metadata: None,
7622 files: USER | PROJECT,
7623 }),
7624 SettingsPageItem::SettingItem(SettingItem {
7625 title: "Use On Type Format",
7626 description: "Whether to use additional LSP queries to format (and amend) the code after every \"trigger\" symbol input, defined by LSP server capabilities",
7627 field: Box::new(SettingField {
7628 json_path: Some("languages.$(language).use_on_type_format"),
7629 pick: |settings_content| {
7630 language_settings_field(settings_content, |language| {
7631 language.use_on_type_format.as_ref()
7632 })
7633 },
7634 write: |settings_content, value| {
7635 language_settings_field_mut(settings_content, value, |language, value| {
7636 language.use_on_type_format = value;
7637 })
7638 },
7639 }),
7640 metadata: None,
7641 files: USER | PROJECT,
7642 }),
7643 SettingsPageItem::SettingItem(SettingItem {
7644 title: "Code Actions On Format",
7645 description: "Additional code actions to run when formatting.",
7646 field: Box::new(
7647 SettingField {
7648 json_path: Some("languages.$(language).code_actions_on_format"),
7649 pick: |settings_content| {
7650 language_settings_field(settings_content, |language| {
7651 language.code_actions_on_format.as_ref()
7652 })
7653 },
7654 write: |settings_content, value| {
7655 language_settings_field_mut(
7656 settings_content,
7657 value,
7658 |language, value| {
7659 language.code_actions_on_format = value;
7660 },
7661 )
7662 },
7663 }
7664 .unimplemented(),
7665 ),
7666 metadata: None,
7667 files: USER | PROJECT,
7668 }),
7669 ]
7670 }
7671
7672 fn autoclose_section() -> [SettingsPageItem; 5] {
7673 [
7674 SettingsPageItem::SectionHeader("Autoclose"),
7675 SettingsPageItem::SettingItem(SettingItem {
7676 title: "Use Autoclose",
7677 description: "Whether to automatically type closing characters for you. For example, when you type '(', Zed will automatically add a closing ')' at the correct position.",
7678 field: Box::new(SettingField {
7679 json_path: Some("languages.$(language).use_autoclose"),
7680 pick: |settings_content| {
7681 language_settings_field(settings_content, |language| {
7682 language.use_autoclose.as_ref()
7683 })
7684 },
7685 write: |settings_content, value| {
7686 language_settings_field_mut(settings_content, value, |language, value| {
7687 language.use_autoclose = value;
7688 })
7689 },
7690 }),
7691 metadata: None,
7692 files: USER | PROJECT,
7693 }),
7694 SettingsPageItem::SettingItem(SettingItem {
7695 title: "Use Auto Surround",
7696 description: "Whether to automatically surround text with characters for you. For example, when you select text and type '(', Zed will automatically surround text with ().",
7697 field: Box::new(SettingField {
7698 json_path: Some("languages.$(language).use_auto_surround"),
7699 pick: |settings_content| {
7700 language_settings_field(settings_content, |language| {
7701 language.use_auto_surround.as_ref()
7702 })
7703 },
7704 write: |settings_content, value| {
7705 language_settings_field_mut(settings_content, value, |language, value| {
7706 language.use_auto_surround = value;
7707 })
7708 },
7709 }),
7710 metadata: None,
7711 files: USER | PROJECT,
7712 }),
7713 SettingsPageItem::SettingItem(SettingItem {
7714 title: "Always Treat Brackets As Autoclosed",
7715 description: "Controls whether the closing characters are always skipped over and auto-removed no matter how they were inserted.",
7716 field: Box::new(SettingField {
7717 json_path: Some("languages.$(language).always_treat_brackets_as_autoclosed"),
7718 pick: |settings_content| {
7719 language_settings_field(settings_content, |language| {
7720 language.always_treat_brackets_as_autoclosed.as_ref()
7721 })
7722 },
7723 write: |settings_content, value| {
7724 language_settings_field_mut(settings_content, value, |language, value| {
7725 language.always_treat_brackets_as_autoclosed = value;
7726 })
7727 },
7728 }),
7729 metadata: None,
7730 files: USER | PROJECT,
7731 }),
7732 SettingsPageItem::SettingItem(SettingItem {
7733 title: "JSX Tag Auto Close",
7734 description: "Whether to automatically close JSX tags.",
7735 field: Box::new(SettingField {
7736 json_path: Some("languages.$(language).jsx_tag_auto_close"),
7737 // TODO(settings_ui): this setting should just be a bool
7738 pick: |settings_content| {
7739 language_settings_field(settings_content, |language| {
7740 language.jsx_tag_auto_close.as_ref()?.enabled.as_ref()
7741 })
7742 },
7743 write: |settings_content, value| {
7744 language_settings_field_mut(settings_content, value, |language, value| {
7745 language.jsx_tag_auto_close.get_or_insert_default().enabled = value;
7746 })
7747 },
7748 }),
7749 metadata: None,
7750 files: USER | PROJECT,
7751 }),
7752 ]
7753 }
7754
7755 fn whitespace_section() -> [SettingsPageItem; 4] {
7756 [
7757 SettingsPageItem::SectionHeader("Whitespace"),
7758 SettingsPageItem::SettingItem(SettingItem {
7759 title: "Show Whitespaces",
7760 description: "Whether to show tabs and spaces in the editor.",
7761 field: Box::new(SettingField {
7762 json_path: Some("languages.$(language).show_whitespaces"),
7763 pick: |settings_content| {
7764 language_settings_field(settings_content, |language| {
7765 language.show_whitespaces.as_ref()
7766 })
7767 },
7768 write: |settings_content, value| {
7769 language_settings_field_mut(settings_content, value, |language, value| {
7770 language.show_whitespaces = value;
7771 })
7772 },
7773 }),
7774 metadata: None,
7775 files: USER | PROJECT,
7776 }),
7777 SettingsPageItem::SettingItem(SettingItem {
7778 title: "Space Whitespace Indicator",
7779 description: "Visible character used to render space characters when show_whitespaces is enabled (default: \"•\")",
7780 field: Box::new(
7781 SettingField {
7782 json_path: Some("languages.$(language).whitespace_map.space"),
7783 pick: |settings_content| {
7784 language_settings_field(settings_content, |language| {
7785 language.whitespace_map.as_ref()?.space.as_ref()
7786 })
7787 },
7788 write: |settings_content, value| {
7789 language_settings_field_mut(
7790 settings_content,
7791 value,
7792 |language, value| {
7793 language.whitespace_map.get_or_insert_default().space = value;
7794 },
7795 )
7796 },
7797 }
7798 .unimplemented(),
7799 ),
7800 metadata: None,
7801 files: USER | PROJECT,
7802 }),
7803 SettingsPageItem::SettingItem(SettingItem {
7804 title: "Tab Whitespace Indicator",
7805 description: "Visible character used to render tab characters when show_whitespaces is enabled (default: \"→\")",
7806 field: Box::new(
7807 SettingField {
7808 json_path: Some("languages.$(language).whitespace_map.tab"),
7809 pick: |settings_content| {
7810 language_settings_field(settings_content, |language| {
7811 language.whitespace_map.as_ref()?.tab.as_ref()
7812 })
7813 },
7814 write: |settings_content, value| {
7815 language_settings_field_mut(
7816 settings_content,
7817 value,
7818 |language, value| {
7819 language.whitespace_map.get_or_insert_default().tab = value;
7820 },
7821 )
7822 },
7823 }
7824 .unimplemented(),
7825 ),
7826 metadata: None,
7827 files: USER | PROJECT,
7828 }),
7829 ]
7830 }
7831
7832 fn completions_section() -> [SettingsPageItem; 7] {
7833 [
7834 SettingsPageItem::SectionHeader("Completions"),
7835 SettingsPageItem::SettingItem(SettingItem {
7836 title: "Show Completions On Input",
7837 description: "Whether to pop the completions menu while typing in an editor without explicitly requesting it.",
7838 field: Box::new(SettingField {
7839 json_path: Some("languages.$(language).show_completions_on_input"),
7840 pick: |settings_content| {
7841 language_settings_field(settings_content, |language| {
7842 language.show_completions_on_input.as_ref()
7843 })
7844 },
7845 write: |settings_content, value| {
7846 language_settings_field_mut(settings_content, value, |language, value| {
7847 language.show_completions_on_input = value;
7848 })
7849 },
7850 }),
7851 metadata: None,
7852 files: USER | PROJECT,
7853 }),
7854 SettingsPageItem::SettingItem(SettingItem {
7855 title: "Show Completion Documentation",
7856 description: "Whether to display inline and alongside documentation for items in the completions menu.",
7857 field: Box::new(SettingField {
7858 json_path: Some("languages.$(language).show_completion_documentation"),
7859 pick: |settings_content| {
7860 language_settings_field(settings_content, |language| {
7861 language.show_completion_documentation.as_ref()
7862 })
7863 },
7864 write: |settings_content, value| {
7865 language_settings_field_mut(settings_content, value, |language, value| {
7866 language.show_completion_documentation = value;
7867 })
7868 },
7869 }),
7870 metadata: None,
7871 files: USER | PROJECT,
7872 }),
7873 SettingsPageItem::SettingItem(SettingItem {
7874 title: "Words",
7875 description: "Controls how words are completed.",
7876 field: Box::new(SettingField {
7877 json_path: Some("languages.$(language).completions.words"),
7878 pick: |settings_content| {
7879 language_settings_field(settings_content, |language| {
7880 language.completions.as_ref()?.words.as_ref()
7881 })
7882 },
7883 write: |settings_content, value| {
7884 language_settings_field_mut(settings_content, value, |language, value| {
7885 language.completions.get_or_insert_default().words = value;
7886 })
7887 },
7888 }),
7889 metadata: None,
7890 files: USER | PROJECT,
7891 }),
7892 SettingsPageItem::SettingItem(SettingItem {
7893 title: "Words Min Length",
7894 description: "How many characters has to be in the completions query to automatically show the words-based completions.",
7895 field: Box::new(SettingField {
7896 json_path: Some("languages.$(language).completions.words_min_length"),
7897 pick: |settings_content| {
7898 language_settings_field(settings_content, |language| {
7899 language.completions.as_ref()?.words_min_length.as_ref()
7900 })
7901 },
7902 write: |settings_content, value| {
7903 language_settings_field_mut(settings_content, value, |language, value| {
7904 language
7905 .completions
7906 .get_or_insert_default()
7907 .words_min_length = value;
7908 })
7909 },
7910 }),
7911 metadata: None,
7912 files: USER | PROJECT,
7913 }),
7914 SettingsPageItem::SettingItem(SettingItem {
7915 title: "Completion Menu Scrollbar",
7916 description: "When to show the scrollbar in the completion menu.",
7917 field: Box::new(SettingField {
7918 json_path: Some("editor.completion_menu_scrollbar"),
7919 pick: |settings_content| {
7920 settings_content.editor.completion_menu_scrollbar.as_ref()
7921 },
7922 write: |settings_content, value| {
7923 settings_content.editor.completion_menu_scrollbar = value;
7924 },
7925 }),
7926 metadata: None,
7927 files: USER,
7928 }),
7929 SettingsPageItem::SettingItem(SettingItem {
7930 title: "Completion Detail Alignment",
7931 description: "Whether to align detail text in code completions context menus left or right.",
7932 field: Box::new(SettingField {
7933 json_path: Some("editor.completion_detail_alignment"),
7934 pick: |settings_content| {
7935 settings_content.editor.completion_detail_alignment.as_ref()
7936 },
7937 write: |settings_content, value| {
7938 settings_content.editor.completion_detail_alignment = value;
7939 },
7940 }),
7941 metadata: None,
7942 files: USER,
7943 }),
7944 ]
7945 }
7946
7947 fn inlay_hints_section() -> [SettingsPageItem; 10] {
7948 [
7949 SettingsPageItem::SectionHeader("Inlay Hints"),
7950 SettingsPageItem::SettingItem(SettingItem {
7951 title: "Enabled",
7952 description: "Global switch to toggle hints on and off.",
7953 field: Box::new(SettingField {
7954 json_path: Some("languages.$(language).inlay_hints.enabled"),
7955 pick: |settings_content| {
7956 language_settings_field(settings_content, |language| {
7957 language.inlay_hints.as_ref()?.enabled.as_ref()
7958 })
7959 },
7960 write: |settings_content, value| {
7961 language_settings_field_mut(settings_content, value, |language, value| {
7962 language.inlay_hints.get_or_insert_default().enabled = value;
7963 })
7964 },
7965 }),
7966 metadata: None,
7967 files: USER | PROJECT,
7968 }),
7969 SettingsPageItem::SettingItem(SettingItem {
7970 title: "Show Value Hints",
7971 description: "Global switch to toggle inline values on and off when debugging.",
7972 field: Box::new(SettingField {
7973 json_path: Some("languages.$(language).inlay_hints.show_value_hints"),
7974 pick: |settings_content| {
7975 language_settings_field(settings_content, |language| {
7976 language.inlay_hints.as_ref()?.show_value_hints.as_ref()
7977 })
7978 },
7979 write: |settings_content, value| {
7980 language_settings_field_mut(settings_content, value, |language, value| {
7981 language
7982 .inlay_hints
7983 .get_or_insert_default()
7984 .show_value_hints = value;
7985 })
7986 },
7987 }),
7988 metadata: None,
7989 files: USER | PROJECT,
7990 }),
7991 SettingsPageItem::SettingItem(SettingItem {
7992 title: "Show Type Hints",
7993 description: "Whether type hints should be shown.",
7994 field: Box::new(SettingField {
7995 json_path: Some("languages.$(language).inlay_hints.show_type_hints"),
7996 pick: |settings_content| {
7997 language_settings_field(settings_content, |language| {
7998 language.inlay_hints.as_ref()?.show_type_hints.as_ref()
7999 })
8000 },
8001 write: |settings_content, value| {
8002 language_settings_field_mut(settings_content, value, |language, value| {
8003 language.inlay_hints.get_or_insert_default().show_type_hints = value;
8004 })
8005 },
8006 }),
8007 metadata: None,
8008 files: USER | PROJECT,
8009 }),
8010 SettingsPageItem::SettingItem(SettingItem {
8011 title: "Show Parameter Hints",
8012 description: "Whether parameter hints should be shown.",
8013 field: Box::new(SettingField {
8014 json_path: Some("languages.$(language).inlay_hints.show_parameter_hints"),
8015 pick: |settings_content| {
8016 language_settings_field(settings_content, |language| {
8017 language.inlay_hints.as_ref()?.show_parameter_hints.as_ref()
8018 })
8019 },
8020 write: |settings_content, value| {
8021 language_settings_field_mut(settings_content, value, |language, value| {
8022 language
8023 .inlay_hints
8024 .get_or_insert_default()
8025 .show_parameter_hints = value;
8026 })
8027 },
8028 }),
8029 metadata: None,
8030 files: USER | PROJECT,
8031 }),
8032 SettingsPageItem::SettingItem(SettingItem {
8033 title: "Show Other Hints",
8034 description: "Whether other hints should be shown.",
8035 field: Box::new(SettingField {
8036 json_path: Some("languages.$(language).inlay_hints.show_other_hints"),
8037 pick: |settings_content| {
8038 language_settings_field(settings_content, |language| {
8039 language.inlay_hints.as_ref()?.show_other_hints.as_ref()
8040 })
8041 },
8042 write: |settings_content, value| {
8043 language_settings_field_mut(settings_content, value, |language, value| {
8044 language
8045 .inlay_hints
8046 .get_or_insert_default()
8047 .show_other_hints = value;
8048 })
8049 },
8050 }),
8051 metadata: None,
8052 files: USER | PROJECT,
8053 }),
8054 SettingsPageItem::SettingItem(SettingItem {
8055 title: "Show Background",
8056 description: "Show a background for inlay hints.",
8057 field: Box::new(SettingField {
8058 json_path: Some("languages.$(language).inlay_hints.show_background"),
8059 pick: |settings_content| {
8060 language_settings_field(settings_content, |language| {
8061 language.inlay_hints.as_ref()?.show_background.as_ref()
8062 })
8063 },
8064 write: |settings_content, value| {
8065 language_settings_field_mut(settings_content, value, |language, value| {
8066 language.inlay_hints.get_or_insert_default().show_background = value;
8067 })
8068 },
8069 }),
8070 metadata: None,
8071 files: USER | PROJECT,
8072 }),
8073 SettingsPageItem::SettingItem(SettingItem {
8074 title: "Edit Debounce Ms",
8075 description: "Whether or not to debounce inlay hints updates after buffer edits (set to 0 to disable debouncing).",
8076 field: Box::new(SettingField {
8077 json_path: Some("languages.$(language).inlay_hints.edit_debounce_ms"),
8078 pick: |settings_content| {
8079 language_settings_field(settings_content, |language| {
8080 language.inlay_hints.as_ref()?.edit_debounce_ms.as_ref()
8081 })
8082 },
8083 write: |settings_content, value| {
8084 language_settings_field_mut(settings_content, value, |language, value| {
8085 language
8086 .inlay_hints
8087 .get_or_insert_default()
8088 .edit_debounce_ms = value;
8089 })
8090 },
8091 }),
8092 metadata: None,
8093 files: USER | PROJECT,
8094 }),
8095 SettingsPageItem::SettingItem(SettingItem {
8096 title: "Scroll Debounce Ms",
8097 description: "Whether or not to debounce inlay hints updates after buffer scrolls (set to 0 to disable debouncing).",
8098 field: Box::new(SettingField {
8099 json_path: Some("languages.$(language).inlay_hints.scroll_debounce_ms"),
8100 pick: |settings_content| {
8101 language_settings_field(settings_content, |language| {
8102 language.inlay_hints.as_ref()?.scroll_debounce_ms.as_ref()
8103 })
8104 },
8105 write: |settings_content, value| {
8106 language_settings_field_mut(settings_content, value, |language, value| {
8107 language
8108 .inlay_hints
8109 .get_or_insert_default()
8110 .scroll_debounce_ms = value;
8111 })
8112 },
8113 }),
8114 metadata: None,
8115 files: USER | PROJECT,
8116 }),
8117 SettingsPageItem::SettingItem(SettingItem {
8118 title: "Toggle On Modifiers Press",
8119 description: "Toggles inlay hints (hides or shows) when the user presses the modifiers specified.",
8120 field: Box::new(
8121 SettingField {
8122 json_path: Some(
8123 "languages.$(language).inlay_hints.toggle_on_modifiers_press",
8124 ),
8125 pick: |settings_content| {
8126 language_settings_field(settings_content, |language| {
8127 language
8128 .inlay_hints
8129 .as_ref()?
8130 .toggle_on_modifiers_press
8131 .as_ref()
8132 })
8133 },
8134 write: |settings_content, value| {
8135 language_settings_field_mut(
8136 settings_content,
8137 value,
8138 |language, value| {
8139 language
8140 .inlay_hints
8141 .get_or_insert_default()
8142 .toggle_on_modifiers_press = value;
8143 },
8144 )
8145 },
8146 }
8147 .unimplemented(),
8148 ),
8149 metadata: None,
8150 files: USER | PROJECT,
8151 }),
8152 ]
8153 }
8154
8155 fn tasks_section() -> [SettingsPageItem; 4] {
8156 [
8157 SettingsPageItem::SectionHeader("Tasks"),
8158 SettingsPageItem::SettingItem(SettingItem {
8159 title: "Enabled",
8160 description: "Whether tasks are enabled for this language.",
8161 field: Box::new(SettingField {
8162 json_path: Some("languages.$(language).tasks.enabled"),
8163 pick: |settings_content| {
8164 language_settings_field(settings_content, |language| {
8165 language.tasks.as_ref()?.enabled.as_ref()
8166 })
8167 },
8168 write: |settings_content, value| {
8169 language_settings_field_mut(settings_content, value, |language, value| {
8170 language.tasks.get_or_insert_default().enabled = value;
8171 })
8172 },
8173 }),
8174 metadata: None,
8175 files: USER | PROJECT,
8176 }),
8177 SettingsPageItem::SettingItem(SettingItem {
8178 title: "Variables",
8179 description: "Extra task variables to set for a particular language.",
8180 field: Box::new(
8181 SettingField {
8182 json_path: Some("languages.$(language).tasks.variables"),
8183 pick: |settings_content| {
8184 language_settings_field(settings_content, |language| {
8185 language.tasks.as_ref()?.variables.as_ref()
8186 })
8187 },
8188 write: |settings_content, value| {
8189 language_settings_field_mut(
8190 settings_content,
8191 value,
8192 |language, value| {
8193 language.tasks.get_or_insert_default().variables = value;
8194 },
8195 )
8196 },
8197 }
8198 .unimplemented(),
8199 ),
8200 metadata: None,
8201 files: USER | PROJECT,
8202 }),
8203 SettingsPageItem::SettingItem(SettingItem {
8204 title: "Prefer LSP",
8205 description: "Use LSP tasks over Zed language extension tasks.",
8206 field: Box::new(SettingField {
8207 json_path: Some("languages.$(language).tasks.prefer_lsp"),
8208 pick: |settings_content| {
8209 language_settings_field(settings_content, |language| {
8210 language.tasks.as_ref()?.prefer_lsp.as_ref()
8211 })
8212 },
8213 write: |settings_content, value| {
8214 language_settings_field_mut(settings_content, value, |language, value| {
8215 language.tasks.get_or_insert_default().prefer_lsp = value;
8216 })
8217 },
8218 }),
8219 metadata: None,
8220 files: USER | PROJECT,
8221 }),
8222 ]
8223 }
8224
8225 fn miscellaneous_section() -> [SettingsPageItem; 6] {
8226 [
8227 SettingsPageItem::SectionHeader("Miscellaneous"),
8228 SettingsPageItem::SettingItem(SettingItem {
8229 title: "Word Diff Enabled",
8230 description: "Whether to enable word diff highlighting in the editor. When enabled, changed words within modified lines are highlighted to show exactly what changed.",
8231 field: Box::new(SettingField {
8232 json_path: Some("languages.$(language).word_diff_enabled"),
8233 pick: |settings_content| {
8234 language_settings_field(settings_content, |language| {
8235 language.word_diff_enabled.as_ref()
8236 })
8237 },
8238 write: |settings_content, value| {
8239 language_settings_field_mut(settings_content, value, |language, value| {
8240 language.word_diff_enabled = value;
8241 })
8242 },
8243 }),
8244 metadata: None,
8245 files: USER | PROJECT,
8246 }),
8247 SettingsPageItem::SettingItem(SettingItem {
8248 title: "Debuggers",
8249 description: "Preferred debuggers for this language.",
8250 field: Box::new(
8251 SettingField {
8252 json_path: Some("languages.$(language).debuggers"),
8253 pick: |settings_content| {
8254 language_settings_field(settings_content, |language| {
8255 language.debuggers.as_ref()
8256 })
8257 },
8258 write: |settings_content, value| {
8259 language_settings_field_mut(
8260 settings_content,
8261 value,
8262 |language, value| {
8263 language.debuggers = value;
8264 },
8265 )
8266 },
8267 }
8268 .unimplemented(),
8269 ),
8270 metadata: None,
8271 files: USER | PROJECT,
8272 }),
8273 SettingsPageItem::SettingItem(SettingItem {
8274 title: "Middle Click Paste",
8275 description: "Enable middle-click paste on Linux.",
8276 field: Box::new(SettingField {
8277 json_path: Some("languages.$(language).editor.middle_click_paste"),
8278 pick: |settings_content| settings_content.editor.middle_click_paste.as_ref(),
8279 write: |settings_content, value| {
8280 settings_content.editor.middle_click_paste = value;
8281 },
8282 }),
8283 metadata: None,
8284 files: USER,
8285 }),
8286 SettingsPageItem::SettingItem(SettingItem {
8287 title: "Extend Comment On Newline",
8288 description: "Whether to start a new line with a comment when a previous line is a comment as well.",
8289 field: Box::new(SettingField {
8290 json_path: Some("languages.$(language).extend_comment_on_newline"),
8291 pick: |settings_content| {
8292 language_settings_field(settings_content, |language| {
8293 language.extend_comment_on_newline.as_ref()
8294 })
8295 },
8296 write: |settings_content, value| {
8297 language_settings_field_mut(settings_content, value, |language, value| {
8298 language.extend_comment_on_newline = value;
8299 })
8300 },
8301 }),
8302 metadata: None,
8303 files: USER | PROJECT,
8304 }),
8305 SettingsPageItem::SettingItem(SettingItem {
8306 title: "Colorize Brackets",
8307 description: "Whether to colorize brackets in the editor.",
8308 field: Box::new(SettingField {
8309 json_path: Some("languages.$(language).colorize_brackets"),
8310 pick: |settings_content| {
8311 language_settings_field(settings_content, |language| {
8312 language.colorize_brackets.as_ref()
8313 })
8314 },
8315 write: |settings_content, value| {
8316 language_settings_field_mut(settings_content, value, |language, value| {
8317 language.colorize_brackets = value;
8318 })
8319 },
8320 }),
8321 metadata: None,
8322 files: USER | PROJECT,
8323 }),
8324 ]
8325 }
8326
8327 fn global_only_miscellaneous_sub_section() -> [SettingsPageItem; 3] {
8328 [
8329 SettingsPageItem::SettingItem(SettingItem {
8330 title: "Image Viewer",
8331 description: "The unit for image file sizes.",
8332 field: Box::new(SettingField {
8333 json_path: Some("image_viewer.unit"),
8334 pick: |settings_content| {
8335 settings_content
8336 .image_viewer
8337 .as_ref()
8338 .and_then(|image_viewer| image_viewer.unit.as_ref())
8339 },
8340 write: |settings_content, value| {
8341 settings_content.image_viewer.get_or_insert_default().unit = value;
8342 },
8343 }),
8344 metadata: None,
8345 files: USER,
8346 }),
8347 SettingsPageItem::SettingItem(SettingItem {
8348 title: "Auto Replace Emoji Shortcode",
8349 description: "Whether to automatically replace emoji shortcodes with emoji characters.",
8350 field: Box::new(SettingField {
8351 json_path: Some("message_editor.auto_replace_emoji_shortcode"),
8352 pick: |settings_content| {
8353 settings_content
8354 .message_editor
8355 .as_ref()
8356 .and_then(|message_editor| {
8357 message_editor.auto_replace_emoji_shortcode.as_ref()
8358 })
8359 },
8360 write: |settings_content, value| {
8361 settings_content
8362 .message_editor
8363 .get_or_insert_default()
8364 .auto_replace_emoji_shortcode = value;
8365 },
8366 }),
8367 metadata: None,
8368 files: USER,
8369 }),
8370 SettingsPageItem::SettingItem(SettingItem {
8371 title: "Drop Size Target",
8372 description: "Relative size of the drop target in the editor that will open dropped file as a split pane.",
8373 field: Box::new(SettingField {
8374 json_path: Some("drop_target_size"),
8375 pick: |settings_content| settings_content.workspace.drop_target_size.as_ref(),
8376 write: |settings_content, value| {
8377 settings_content.workspace.drop_target_size = value;
8378 },
8379 }),
8380 metadata: None,
8381 files: USER,
8382 }),
8383 ]
8384 }
8385
8386 let is_global = current_language().is_none();
8387
8388 let lsp_document_colors_item = [SettingsPageItem::SettingItem(SettingItem {
8389 title: "LSP Document Colors",
8390 description: "How to render LSP color previews in the editor.",
8391 field: Box::new(SettingField {
8392 json_path: Some("lsp_document_colors"),
8393 pick: |settings_content| settings_content.editor.lsp_document_colors.as_ref(),
8394 write: |settings_content, value| {
8395 settings_content.editor.lsp_document_colors = value;
8396 },
8397 }),
8398 metadata: None,
8399 files: USER,
8400 })];
8401
8402 if is_global {
8403 concat_sections!(
8404 indentation_section(),
8405 wrapping_section(),
8406 indent_guides_section(),
8407 formatting_section(),
8408 autoclose_section(),
8409 whitespace_section(),
8410 completions_section(),
8411 inlay_hints_section(),
8412 lsp_document_colors_item,
8413 tasks_section(),
8414 miscellaneous_section(),
8415 global_only_miscellaneous_sub_section(),
8416 )
8417 } else {
8418 concat_sections!(
8419 indentation_section(),
8420 wrapping_section(),
8421 indent_guides_section(),
8422 formatting_section(),
8423 autoclose_section(),
8424 whitespace_section(),
8425 completions_section(),
8426 inlay_hints_section(),
8427 tasks_section(),
8428 miscellaneous_section(),
8429 )
8430 }
8431}
8432
8433/// LanguageSettings items that should be included in the "Languages & Tools" page
8434/// not the "Editor" page
8435fn non_editor_language_settings_data() -> Box<[SettingsPageItem]> {
8436 fn lsp_section() -> [SettingsPageItem; 5] {
8437 [
8438 SettingsPageItem::SectionHeader("LSP"),
8439 SettingsPageItem::SettingItem(SettingItem {
8440 title: "Enable Language Server",
8441 description: "Whether to use language servers to provide code intelligence.",
8442 field: Box::new(SettingField {
8443 json_path: Some("languages.$(language).enable_language_server"),
8444 pick: |settings_content| {
8445 language_settings_field(settings_content, |language| {
8446 language.enable_language_server.as_ref()
8447 })
8448 },
8449 write: |settings_content, value| {
8450 language_settings_field_mut(settings_content, value, |language, value| {
8451 language.enable_language_server = value;
8452 })
8453 },
8454 }),
8455 metadata: None,
8456 files: USER | PROJECT,
8457 }),
8458 SettingsPageItem::SettingItem(SettingItem {
8459 title: "Language Servers",
8460 description: "The list of language servers to use (or disable) for this language.",
8461 field: Box::new(
8462 SettingField {
8463 json_path: Some("languages.$(language).language_servers"),
8464 pick: |settings_content| {
8465 language_settings_field(settings_content, |language| {
8466 language.language_servers.as_ref()
8467 })
8468 },
8469 write: |settings_content, value| {
8470 language_settings_field_mut(
8471 settings_content,
8472 value,
8473 |language, value| {
8474 language.language_servers = value;
8475 },
8476 )
8477 },
8478 }
8479 .unimplemented(),
8480 ),
8481 metadata: None,
8482 files: USER | PROJECT,
8483 }),
8484 SettingsPageItem::SettingItem(SettingItem {
8485 title: "Linked Edits",
8486 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.",
8487 field: Box::new(SettingField {
8488 json_path: Some("languages.$(language).linked_edits"),
8489 pick: |settings_content| {
8490 language_settings_field(settings_content, |language| {
8491 language.linked_edits.as_ref()
8492 })
8493 },
8494 write: |settings_content, value| {
8495 language_settings_field_mut(settings_content, value, |language, value| {
8496 language.linked_edits = value;
8497 })
8498 },
8499 }),
8500 metadata: None,
8501 files: USER | PROJECT,
8502 }),
8503 SettingsPageItem::SettingItem(SettingItem {
8504 title: "Go To Definition Fallback",
8505 description: "Whether to follow-up empty Go to definition responses from the language server.",
8506 field: Box::new(SettingField {
8507 json_path: Some("go_to_definition_fallback"),
8508 pick: |settings_content| {
8509 settings_content.editor.go_to_definition_fallback.as_ref()
8510 },
8511 write: |settings_content, value| {
8512 settings_content.editor.go_to_definition_fallback = value;
8513 },
8514 }),
8515 metadata: None,
8516 files: USER,
8517 }),
8518 ]
8519 }
8520
8521 fn lsp_completions_section() -> [SettingsPageItem; 4] {
8522 [
8523 SettingsPageItem::SectionHeader("LSP Completions"),
8524 SettingsPageItem::SettingItem(SettingItem {
8525 title: "Enabled",
8526 description: "Whether to fetch LSP completions or not.",
8527 field: Box::new(SettingField {
8528 json_path: Some("languages.$(language).completions.lsp"),
8529 pick: |settings_content| {
8530 language_settings_field(settings_content, |language| {
8531 language.completions.as_ref()?.lsp.as_ref()
8532 })
8533 },
8534 write: |settings_content, value| {
8535 language_settings_field_mut(settings_content, value, |language, value| {
8536 language.completions.get_or_insert_default().lsp = value;
8537 })
8538 },
8539 }),
8540 metadata: None,
8541 files: USER | PROJECT,
8542 }),
8543 SettingsPageItem::SettingItem(SettingItem {
8544 title: "Fetch Timeout (milliseconds)",
8545 description: "When fetching LSP completions, determines how long to wait for a response of a particular server (set to 0 to wait indefinitely).",
8546 field: Box::new(SettingField {
8547 json_path: Some("languages.$(language).completions.lsp_fetch_timeout_ms"),
8548 pick: |settings_content| {
8549 language_settings_field(settings_content, |language| {
8550 language.completions.as_ref()?.lsp_fetch_timeout_ms.as_ref()
8551 })
8552 },
8553 write: |settings_content, value| {
8554 language_settings_field_mut(settings_content, value, |language, value| {
8555 language
8556 .completions
8557 .get_or_insert_default()
8558 .lsp_fetch_timeout_ms = value;
8559 })
8560 },
8561 }),
8562 metadata: None,
8563 files: USER | PROJECT,
8564 }),
8565 SettingsPageItem::SettingItem(SettingItem {
8566 title: "Insert Mode",
8567 description: "Controls how LSP completions are inserted.",
8568 field: Box::new(SettingField {
8569 json_path: Some("languages.$(language).completions.lsp_insert_mode"),
8570 pick: |settings_content| {
8571 language_settings_field(settings_content, |language| {
8572 language.completions.as_ref()?.lsp_insert_mode.as_ref()
8573 })
8574 },
8575 write: |settings_content, value| {
8576 language_settings_field_mut(settings_content, value, |language, value| {
8577 language.completions.get_or_insert_default().lsp_insert_mode = value;
8578 })
8579 },
8580 }),
8581 metadata: None,
8582 files: USER | PROJECT,
8583 }),
8584 ]
8585 }
8586
8587 fn debugger_section() -> [SettingsPageItem; 2] {
8588 [
8589 SettingsPageItem::SectionHeader("Debuggers"),
8590 SettingsPageItem::SettingItem(SettingItem {
8591 title: "Debuggers",
8592 description: "Preferred debuggers for this language.",
8593 field: Box::new(
8594 SettingField {
8595 json_path: Some("languages.$(language).debuggers"),
8596 pick: |settings_content| {
8597 language_settings_field(settings_content, |language| {
8598 language.debuggers.as_ref()
8599 })
8600 },
8601 write: |settings_content, value| {
8602 language_settings_field_mut(
8603 settings_content,
8604 value,
8605 |language, value| {
8606 language.debuggers = value;
8607 },
8608 )
8609 },
8610 }
8611 .unimplemented(),
8612 ),
8613 metadata: None,
8614 files: USER | PROJECT,
8615 }),
8616 ]
8617 }
8618
8619 fn prettier_section() -> [SettingsPageItem; 5] {
8620 [
8621 SettingsPageItem::SectionHeader("Prettier"),
8622 SettingsPageItem::SettingItem(SettingItem {
8623 title: "Allowed",
8624 description: "Enables or disables formatting with Prettier for a given language.",
8625 field: Box::new(SettingField {
8626 json_path: Some("languages.$(language).prettier.allowed"),
8627 pick: |settings_content| {
8628 language_settings_field(settings_content, |language| {
8629 language.prettier.as_ref()?.allowed.as_ref()
8630 })
8631 },
8632 write: |settings_content, value| {
8633 language_settings_field_mut(settings_content, value, |language, value| {
8634 language.prettier.get_or_insert_default().allowed = value;
8635 })
8636 },
8637 }),
8638 metadata: None,
8639 files: USER | PROJECT,
8640 }),
8641 SettingsPageItem::SettingItem(SettingItem {
8642 title: "Parser",
8643 description: "Forces Prettier integration to use a specific parser name when formatting files with the language.",
8644 field: Box::new(SettingField {
8645 json_path: Some("languages.$(language).prettier.parser"),
8646 pick: |settings_content| {
8647 language_settings_field(settings_content, |language| {
8648 language.prettier.as_ref()?.parser.as_ref()
8649 })
8650 },
8651 write: |settings_content, value| {
8652 language_settings_field_mut(settings_content, value, |language, value| {
8653 language.prettier.get_or_insert_default().parser = value;
8654 })
8655 },
8656 }),
8657 metadata: None,
8658 files: USER | PROJECT,
8659 }),
8660 SettingsPageItem::SettingItem(SettingItem {
8661 title: "Plugins",
8662 description: "Forces Prettier integration to use specific plugins when formatting files with the language.",
8663 field: Box::new(
8664 SettingField {
8665 json_path: Some("languages.$(language).prettier.plugins"),
8666 pick: |settings_content| {
8667 language_settings_field(settings_content, |language| {
8668 language.prettier.as_ref()?.plugins.as_ref()
8669 })
8670 },
8671 write: |settings_content, value| {
8672 language_settings_field_mut(
8673 settings_content,
8674 value,
8675 |language, value| {
8676 language.prettier.get_or_insert_default().plugins = value;
8677 },
8678 )
8679 },
8680 }
8681 .unimplemented(),
8682 ),
8683 metadata: None,
8684 files: USER | PROJECT,
8685 }),
8686 SettingsPageItem::SettingItem(SettingItem {
8687 title: "Options",
8688 description: "Default Prettier options, in the format as in package.json section for Prettier.",
8689 field: Box::new(
8690 SettingField {
8691 json_path: Some("languages.$(language).prettier.options"),
8692 pick: |settings_content| {
8693 language_settings_field(settings_content, |language| {
8694 language.prettier.as_ref()?.options.as_ref()
8695 })
8696 },
8697 write: |settings_content, value| {
8698 language_settings_field_mut(
8699 settings_content,
8700 value,
8701 |language, value| {
8702 language.prettier.get_or_insert_default().options = value;
8703 },
8704 )
8705 },
8706 }
8707 .unimplemented(),
8708 ),
8709 metadata: None,
8710 files: USER | PROJECT,
8711 }),
8712 ]
8713 }
8714
8715 concat_sections!(
8716 lsp_section(),
8717 lsp_completions_section(),
8718 debugger_section(),
8719 prettier_section(),
8720 )
8721}
8722
8723fn edit_prediction_language_settings_section() -> [SettingsPageItem; 4] {
8724 [
8725 SettingsPageItem::SectionHeader("Edit Predictions"),
8726 SettingsPageItem::SubPageLink(SubPageLink {
8727 title: "Configure Providers".into(),
8728 json_path: Some("edit_predictions.providers"),
8729 description: Some("Set up different edit prediction providers in complement to Zed's built-in Zeta model.".into()),
8730 in_json: false,
8731 files: USER,
8732 render: Arc::new(|_, window, cx| {
8733 let settings_window = cx.entity();
8734 let page = window.use_state(cx, |_, _| {
8735 crate::pages::EditPredictionSetupPage::new(settings_window)
8736 });
8737 page.into_any_element()
8738 }),
8739 }),
8740 SettingsPageItem::SettingItem(SettingItem {
8741 title: "Show Edit Predictions",
8742 description: "Controls whether edit predictions are shown immediately or manually.",
8743 field: Box::new(SettingField {
8744 json_path: Some("languages.$(language).show_edit_predictions"),
8745 pick: |settings_content| {
8746 language_settings_field(settings_content, |language| {
8747 language.show_edit_predictions.as_ref()
8748 })
8749 },
8750 write: |settings_content, value| {
8751 language_settings_field_mut(settings_content, value, |language, value| {
8752 language.show_edit_predictions = value;
8753 })
8754 },
8755 }),
8756 metadata: None,
8757 files: USER | PROJECT,
8758 }),
8759 SettingsPageItem::SettingItem(SettingItem {
8760 title: "Disable in Language Scopes",
8761 description: "Controls whether edit predictions are shown in the given language scopes.",
8762 field: Box::new(
8763 SettingField {
8764 json_path: Some("languages.$(language).edit_predictions_disabled_in"),
8765 pick: |settings_content| {
8766 language_settings_field(settings_content, |language| {
8767 language.edit_predictions_disabled_in.as_ref()
8768 })
8769 },
8770 write: |settings_content, value| {
8771 language_settings_field_mut(settings_content, value, |language, value| {
8772 language.edit_predictions_disabled_in = value;
8773 })
8774 },
8775 }
8776 .unimplemented(),
8777 ),
8778 metadata: None,
8779 files: USER | PROJECT,
8780 }),
8781 ]
8782}
8783
8784fn show_scrollbar_or_editor(
8785 settings_content: &SettingsContent,
8786 show: fn(&SettingsContent) -> Option<&settings::ShowScrollbar>,
8787) -> Option<&settings::ShowScrollbar> {
8788 show(settings_content).or(settings_content
8789 .editor
8790 .scrollbar
8791 .as_ref()
8792 .and_then(|scrollbar| scrollbar.show.as_ref()))
8793}
8794
8795fn dynamic_variants<T>() -> &'static [T::Discriminant]
8796where
8797 T: strum::IntoDiscriminant,
8798 T::Discriminant: strum::VariantArray,
8799{
8800 <<T as strum::IntoDiscriminant>::Discriminant as strum::VariantArray>::VARIANTS
8801}