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