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