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