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