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