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