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