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