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