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