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