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