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