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