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