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