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