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