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