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