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