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