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; 9] {
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: "Seed Search Query From Cursor",
3138 description: "When to populate a new search's query based on the text under the cursor.",
3139 field: Box::new(SettingField {
3140 json_path: Some("seed_search_query_from_cursor"),
3141 pick: |settings_content| {
3142 settings_content
3143 .editor
3144 .seed_search_query_from_cursor
3145 .as_ref()
3146 },
3147 write: |settings_content, value| {
3148 settings_content.editor.seed_search_query_from_cursor = value;
3149 },
3150 }),
3151 metadata: None,
3152 files: USER,
3153 }),
3154 ]
3155 }
3156
3157 fn file_finder_section() -> [SettingsPageItem; 6] {
3158 [
3159 SettingsPageItem::SectionHeader("File Finder"),
3160 // todo: null by default
3161 SettingsPageItem::SettingItem(SettingItem {
3162 title: "Include Ignored in Search",
3163 description: "Use gitignored files when searching.",
3164 field: Box::new(SettingField {
3165 json_path: Some("file_finder.include_ignored"),
3166 pick: |settings_content| {
3167 settings_content
3168 .file_finder
3169 .as_ref()?
3170 .include_ignored
3171 .as_ref()
3172 },
3173 write: |settings_content, value| {
3174 settings_content
3175 .file_finder
3176 .get_or_insert_default()
3177 .include_ignored = value;
3178 },
3179 }),
3180 metadata: None,
3181 files: USER,
3182 }),
3183 SettingsPageItem::SettingItem(SettingItem {
3184 title: "File Icons",
3185 description: "Show file icons in the file finder.",
3186 field: Box::new(SettingField {
3187 json_path: Some("file_finder.file_icons"),
3188 pick: |settings_content| {
3189 settings_content.file_finder.as_ref()?.file_icons.as_ref()
3190 },
3191 write: |settings_content, value| {
3192 settings_content
3193 .file_finder
3194 .get_or_insert_default()
3195 .file_icons = value;
3196 },
3197 }),
3198 metadata: None,
3199 files: USER,
3200 }),
3201 SettingsPageItem::SettingItem(SettingItem {
3202 title: "Modal Max Width",
3203 description: "Determines how much space the file finder can take up in relation to the available window width.",
3204 field: Box::new(SettingField {
3205 json_path: Some("file_finder.modal_max_width"),
3206 pick: |settings_content| {
3207 settings_content
3208 .file_finder
3209 .as_ref()?
3210 .modal_max_width
3211 .as_ref()
3212 },
3213 write: |settings_content, value| {
3214 settings_content
3215 .file_finder
3216 .get_or_insert_default()
3217 .modal_max_width = value;
3218 },
3219 }),
3220 metadata: None,
3221 files: USER,
3222 }),
3223 SettingsPageItem::SettingItem(SettingItem {
3224 title: "Skip Focus For Active In Search",
3225 description: "Whether the file finder should skip focus for the active file in search results.",
3226 field: Box::new(SettingField {
3227 json_path: Some("file_finder.skip_focus_for_active_in_search"),
3228 pick: |settings_content| {
3229 settings_content
3230 .file_finder
3231 .as_ref()?
3232 .skip_focus_for_active_in_search
3233 .as_ref()
3234 },
3235 write: |settings_content, value| {
3236 settings_content
3237 .file_finder
3238 .get_or_insert_default()
3239 .skip_focus_for_active_in_search = value;
3240 },
3241 }),
3242 metadata: None,
3243 files: USER,
3244 }),
3245 SettingsPageItem::SettingItem(SettingItem {
3246 title: "Git Status",
3247 description: "Show the Git status in the file finder.",
3248 field: Box::new(SettingField {
3249 json_path: Some("file_finder.git_status"),
3250 pick: |settings_content| {
3251 settings_content.file_finder.as_ref()?.git_status.as_ref()
3252 },
3253 write: |settings_content, value| {
3254 settings_content
3255 .file_finder
3256 .get_or_insert_default()
3257 .git_status = value;
3258 },
3259 }),
3260 metadata: None,
3261 files: USER,
3262 }),
3263 ]
3264 }
3265
3266 fn file_scan_section() -> [SettingsPageItem; 5] {
3267 [
3268 SettingsPageItem::SectionHeader("File Scan"),
3269 SettingsPageItem::SettingItem(SettingItem {
3270 title: "File Scan Exclusions",
3271 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\"",
3272 field: Box::new(
3273 SettingField {
3274 json_path: Some("file_scan_exclusions"),
3275 pick: |settings_content| {
3276 settings_content
3277 .project
3278 .worktree
3279 .file_scan_exclusions
3280 .as_ref()
3281 },
3282 write: |settings_content, value| {
3283 settings_content.project.worktree.file_scan_exclusions = value;
3284 },
3285 }
3286 .unimplemented(),
3287 ),
3288 metadata: None,
3289 files: USER,
3290 }),
3291 SettingsPageItem::SettingItem(SettingItem {
3292 title: "File Scan Inclusions",
3293 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",
3294 field: Box::new(
3295 SettingField {
3296 json_path: Some("file_scan_inclusions"),
3297 pick: |settings_content| {
3298 settings_content
3299 .project
3300 .worktree
3301 .file_scan_inclusions
3302 .as_ref()
3303 },
3304 write: |settings_content, value| {
3305 settings_content.project.worktree.file_scan_inclusions = value;
3306 },
3307 }
3308 .unimplemented(),
3309 ),
3310 metadata: None,
3311 files: USER,
3312 }),
3313 SettingsPageItem::SettingItem(SettingItem {
3314 title: "Restore File State",
3315 description: "Restore previous file state when reopening.",
3316 field: Box::new(SettingField {
3317 json_path: Some("restore_on_file_reopen"),
3318 pick: |settings_content| {
3319 settings_content.workspace.restore_on_file_reopen.as_ref()
3320 },
3321 write: |settings_content, value| {
3322 settings_content.workspace.restore_on_file_reopen = value;
3323 },
3324 }),
3325 metadata: None,
3326 files: USER,
3327 }),
3328 SettingsPageItem::SettingItem(SettingItem {
3329 title: "Close on File Delete",
3330 description: "Automatically close files that have been deleted.",
3331 field: Box::new(SettingField {
3332 json_path: Some("close_on_file_delete"),
3333 pick: |settings_content| {
3334 settings_content.workspace.close_on_file_delete.as_ref()
3335 },
3336 write: |settings_content, value| {
3337 settings_content.workspace.close_on_file_delete = value;
3338 },
3339 }),
3340 metadata: None,
3341 files: USER,
3342 }),
3343 ]
3344 }
3345
3346 SettingsPage {
3347 title: "Search & Files",
3348 items: concat_sections![search_section(), file_finder_section(), file_scan_section()],
3349 }
3350}
3351
3352fn window_and_layout_page() -> SettingsPage {
3353 fn status_bar_section() -> [SettingsPageItem; 9] {
3354 [
3355 SettingsPageItem::SectionHeader("Status Bar"),
3356 SettingsPageItem::SettingItem(SettingItem {
3357 title: "Project Panel Button",
3358 description: "Show the project panel button in the status bar.",
3359 field: Box::new(SettingField {
3360 json_path: Some("project_panel.button"),
3361 pick: |settings_content| {
3362 settings_content.project_panel.as_ref()?.button.as_ref()
3363 },
3364 write: |settings_content, value| {
3365 settings_content
3366 .project_panel
3367 .get_or_insert_default()
3368 .button = value;
3369 },
3370 }),
3371 metadata: None,
3372 files: USER,
3373 }),
3374 SettingsPageItem::SettingItem(SettingItem {
3375 title: "Active Language Button",
3376 description: "Show the active language button in the status bar.",
3377 field: Box::new(SettingField {
3378 json_path: Some("status_bar.active_language_button"),
3379 pick: |settings_content| {
3380 settings_content
3381 .status_bar
3382 .as_ref()?
3383 .active_language_button
3384 .as_ref()
3385 },
3386 write: |settings_content, value| {
3387 settings_content
3388 .status_bar
3389 .get_or_insert_default()
3390 .active_language_button = value;
3391 },
3392 }),
3393 metadata: None,
3394 files: USER,
3395 }),
3396 SettingsPageItem::SettingItem(SettingItem {
3397 title: "Active Encoding Button",
3398 description: "Control when to show the active encoding in the status bar.",
3399 field: Box::new(SettingField {
3400 json_path: Some("status_bar.active_encoding_button"),
3401 pick: |settings_content| {
3402 settings_content
3403 .status_bar
3404 .as_ref()?
3405 .active_encoding_button
3406 .as_ref()
3407 },
3408 write: |settings_content, value| {
3409 settings_content
3410 .status_bar
3411 .get_or_insert_default()
3412 .active_encoding_button = value;
3413 },
3414 }),
3415 metadata: None,
3416 files: USER,
3417 }),
3418 SettingsPageItem::SettingItem(SettingItem {
3419 title: "Cursor Position Button",
3420 description: "Show the cursor position button in the status bar.",
3421 field: Box::new(SettingField {
3422 json_path: Some("status_bar.cursor_position_button"),
3423 pick: |settings_content| {
3424 settings_content
3425 .status_bar
3426 .as_ref()?
3427 .cursor_position_button
3428 .as_ref()
3429 },
3430 write: |settings_content, value| {
3431 settings_content
3432 .status_bar
3433 .get_or_insert_default()
3434 .cursor_position_button = value;
3435 },
3436 }),
3437 metadata: None,
3438 files: USER,
3439 }),
3440 SettingsPageItem::SettingItem(SettingItem {
3441 title: "Terminal Button",
3442 description: "Show the terminal button in the status bar.",
3443 field: Box::new(SettingField {
3444 json_path: Some("terminal.button"),
3445 pick: |settings_content| settings_content.terminal.as_ref()?.button.as_ref(),
3446 write: |settings_content, value| {
3447 settings_content.terminal.get_or_insert_default().button = value;
3448 },
3449 }),
3450 metadata: None,
3451 files: USER,
3452 }),
3453 SettingsPageItem::SettingItem(SettingItem {
3454 title: "Diagnostics Button",
3455 description: "Show the project diagnostics button in the status bar.",
3456 field: Box::new(SettingField {
3457 json_path: Some("diagnostics.button"),
3458 pick: |settings_content| settings_content.diagnostics.as_ref()?.button.as_ref(),
3459 write: |settings_content, value| {
3460 settings_content.diagnostics.get_or_insert_default().button = value;
3461 },
3462 }),
3463 metadata: None,
3464 files: USER,
3465 }),
3466 SettingsPageItem::SettingItem(SettingItem {
3467 title: "Project Search Button",
3468 description: "Show the project search button in the status bar.",
3469 field: Box::new(SettingField {
3470 json_path: Some("search.button"),
3471 pick: |settings_content| {
3472 settings_content.editor.search.as_ref()?.button.as_ref()
3473 },
3474 write: |settings_content, value| {
3475 settings_content
3476 .editor
3477 .search
3478 .get_or_insert_default()
3479 .button = value;
3480 },
3481 }),
3482 metadata: None,
3483 files: USER,
3484 }),
3485 SettingsPageItem::SettingItem(SettingItem {
3486 title: "Debugger Button",
3487 description: "Show the debugger button in the status bar.",
3488 field: Box::new(SettingField {
3489 json_path: Some("debugger.button"),
3490 pick: |settings_content| settings_content.debugger.as_ref()?.button.as_ref(),
3491 write: |settings_content, value| {
3492 settings_content.debugger.get_or_insert_default().button = value;
3493 },
3494 }),
3495 metadata: None,
3496 files: USER,
3497 }),
3498 ]
3499 }
3500
3501 fn title_bar_section() -> [SettingsPageItem; 9] {
3502 [
3503 SettingsPageItem::SectionHeader("Title Bar"),
3504 SettingsPageItem::SettingItem(SettingItem {
3505 title: "Show Branch Icon",
3506 description: "Show the branch icon beside branch switcher in the titlebar.",
3507 field: Box::new(SettingField {
3508 json_path: Some("title_bar.show_branch_icon"),
3509 pick: |settings_content| {
3510 settings_content
3511 .title_bar
3512 .as_ref()?
3513 .show_branch_icon
3514 .as_ref()
3515 },
3516 write: |settings_content, value| {
3517 settings_content
3518 .title_bar
3519 .get_or_insert_default()
3520 .show_branch_icon = value;
3521 },
3522 }),
3523 metadata: None,
3524 files: USER,
3525 }),
3526 SettingsPageItem::SettingItem(SettingItem {
3527 title: "Show Branch Name",
3528 description: "Show the branch name button in the titlebar.",
3529 field: Box::new(SettingField {
3530 json_path: Some("title_bar.show_branch_name"),
3531 pick: |settings_content| {
3532 settings_content
3533 .title_bar
3534 .as_ref()?
3535 .show_branch_name
3536 .as_ref()
3537 },
3538 write: |settings_content, value| {
3539 settings_content
3540 .title_bar
3541 .get_or_insert_default()
3542 .show_branch_name = value;
3543 },
3544 }),
3545 metadata: None,
3546 files: USER,
3547 }),
3548 SettingsPageItem::SettingItem(SettingItem {
3549 title: "Show Project Items",
3550 description: "Show the project host and name in the titlebar.",
3551 field: Box::new(SettingField {
3552 json_path: Some("title_bar.show_project_items"),
3553 pick: |settings_content| {
3554 settings_content
3555 .title_bar
3556 .as_ref()?
3557 .show_project_items
3558 .as_ref()
3559 },
3560 write: |settings_content, value| {
3561 settings_content
3562 .title_bar
3563 .get_or_insert_default()
3564 .show_project_items = value;
3565 },
3566 }),
3567 metadata: None,
3568 files: USER,
3569 }),
3570 SettingsPageItem::SettingItem(SettingItem {
3571 title: "Show Onboarding Banner",
3572 description: "Show banners announcing new features in the titlebar.",
3573 field: Box::new(SettingField {
3574 json_path: Some("title_bar.show_onboarding_banner"),
3575 pick: |settings_content| {
3576 settings_content
3577 .title_bar
3578 .as_ref()?
3579 .show_onboarding_banner
3580 .as_ref()
3581 },
3582 write: |settings_content, value| {
3583 settings_content
3584 .title_bar
3585 .get_or_insert_default()
3586 .show_onboarding_banner = value;
3587 },
3588 }),
3589 metadata: None,
3590 files: USER,
3591 }),
3592 SettingsPageItem::SettingItem(SettingItem {
3593 title: "Show Sign In",
3594 description: "Show the sign in button in the titlebar.",
3595 field: Box::new(SettingField {
3596 json_path: Some("title_bar.show_sign_in"),
3597 pick: |settings_content| {
3598 settings_content.title_bar.as_ref()?.show_sign_in.as_ref()
3599 },
3600 write: |settings_content, value| {
3601 settings_content
3602 .title_bar
3603 .get_or_insert_default()
3604 .show_sign_in = value;
3605 },
3606 }),
3607 metadata: None,
3608 files: USER,
3609 }),
3610 SettingsPageItem::SettingItem(SettingItem {
3611 title: "Show User Menu",
3612 description: "Show the user menu button in the titlebar.",
3613 field: Box::new(SettingField {
3614 json_path: Some("title_bar.show_user_menu"),
3615 pick: |settings_content| {
3616 settings_content.title_bar.as_ref()?.show_user_menu.as_ref()
3617 },
3618 write: |settings_content, value| {
3619 settings_content
3620 .title_bar
3621 .get_or_insert_default()
3622 .show_user_menu = value;
3623 },
3624 }),
3625 metadata: None,
3626 files: USER,
3627 }),
3628 SettingsPageItem::SettingItem(SettingItem {
3629 title: "Show User Picture",
3630 description: "Show user picture in the titlebar.",
3631 field: Box::new(SettingField {
3632 json_path: Some("title_bar.show_user_picture"),
3633 pick: |settings_content| {
3634 settings_content
3635 .title_bar
3636 .as_ref()?
3637 .show_user_picture
3638 .as_ref()
3639 },
3640 write: |settings_content, value| {
3641 settings_content
3642 .title_bar
3643 .get_or_insert_default()
3644 .show_user_picture = value;
3645 },
3646 }),
3647 metadata: None,
3648 files: USER,
3649 }),
3650 SettingsPageItem::SettingItem(SettingItem {
3651 title: "Show Menus",
3652 description: "Show the menus in the titlebar.",
3653 field: Box::new(SettingField {
3654 json_path: Some("title_bar.show_menus"),
3655 pick: |settings_content| {
3656 settings_content.title_bar.as_ref()?.show_menus.as_ref()
3657 },
3658 write: |settings_content, value| {
3659 settings_content
3660 .title_bar
3661 .get_or_insert_default()
3662 .show_menus = value;
3663 },
3664 }),
3665 metadata: None,
3666 files: USER,
3667 }),
3668 ]
3669 }
3670
3671 fn tab_bar_section() -> [SettingsPageItem; 9] {
3672 [
3673 SettingsPageItem::SectionHeader("Tab Bar"),
3674 SettingsPageItem::SettingItem(SettingItem {
3675 title: "Show Tab Bar",
3676 description: "Show the tab bar in the editor.",
3677 field: Box::new(SettingField {
3678 json_path: Some("tab_bar.show"),
3679 pick: |settings_content| settings_content.tab_bar.as_ref()?.show.as_ref(),
3680 write: |settings_content, value| {
3681 settings_content.tab_bar.get_or_insert_default().show = value;
3682 },
3683 }),
3684 metadata: None,
3685 files: USER,
3686 }),
3687 SettingsPageItem::SettingItem(SettingItem {
3688 title: "Show Git Status In Tabs",
3689 description: "Show the Git file status on a tab item.",
3690 field: Box::new(SettingField {
3691 json_path: Some("tabs.git_status"),
3692 pick: |settings_content| settings_content.tabs.as_ref()?.git_status.as_ref(),
3693 write: |settings_content, value| {
3694 settings_content.tabs.get_or_insert_default().git_status = value;
3695 },
3696 }),
3697 metadata: None,
3698 files: USER,
3699 }),
3700 SettingsPageItem::SettingItem(SettingItem {
3701 title: "Show File Icons In Tabs",
3702 description: "Show the file icon for a tab.",
3703 field: Box::new(SettingField {
3704 json_path: Some("tabs.file_icons"),
3705 pick: |settings_content| settings_content.tabs.as_ref()?.file_icons.as_ref(),
3706 write: |settings_content, value| {
3707 settings_content.tabs.get_or_insert_default().file_icons = value;
3708 },
3709 }),
3710 metadata: None,
3711 files: USER,
3712 }),
3713 SettingsPageItem::SettingItem(SettingItem {
3714 title: "Tab Close Position",
3715 description: "Position of the close button in a tab.",
3716 field: Box::new(SettingField {
3717 json_path: Some("tabs.close_position"),
3718 pick: |settings_content| {
3719 settings_content.tabs.as_ref()?.close_position.as_ref()
3720 },
3721 write: |settings_content, value| {
3722 settings_content.tabs.get_or_insert_default().close_position = value;
3723 },
3724 }),
3725 metadata: None,
3726 files: USER,
3727 }),
3728 SettingsPageItem::SettingItem(SettingItem {
3729 files: USER,
3730 title: "Maximum Tabs",
3731 description: "Maximum open tabs in a pane. Will not close an unsaved tab.",
3732 // todo(settings_ui): The default for this value is null and it's use in code
3733 // is complex, so I'm going to come back to this later
3734 field: Box::new(
3735 SettingField {
3736 json_path: Some("max_tabs"),
3737 pick: |settings_content| settings_content.workspace.max_tabs.as_ref(),
3738 write: |settings_content, value| {
3739 settings_content.workspace.max_tabs = value;
3740 },
3741 }
3742 .unimplemented(),
3743 ),
3744 metadata: None,
3745 }),
3746 SettingsPageItem::SettingItem(SettingItem {
3747 title: "Show Navigation History Buttons",
3748 description: "Show the navigation history buttons in the tab bar.",
3749 field: Box::new(SettingField {
3750 json_path: Some("tab_bar.show_nav_history_buttons"),
3751 pick: |settings_content| {
3752 settings_content
3753 .tab_bar
3754 .as_ref()?
3755 .show_nav_history_buttons
3756 .as_ref()
3757 },
3758 write: |settings_content, value| {
3759 settings_content
3760 .tab_bar
3761 .get_or_insert_default()
3762 .show_nav_history_buttons = value;
3763 },
3764 }),
3765 metadata: None,
3766 files: USER,
3767 }),
3768 SettingsPageItem::SettingItem(SettingItem {
3769 title: "Show Tab Bar Buttons",
3770 description: "Show the tab bar buttons (New, Split Pane, Zoom).",
3771 field: Box::new(SettingField {
3772 json_path: Some("tab_bar.show_tab_bar_buttons"),
3773 pick: |settings_content| {
3774 settings_content
3775 .tab_bar
3776 .as_ref()?
3777 .show_tab_bar_buttons
3778 .as_ref()
3779 },
3780 write: |settings_content, value| {
3781 settings_content
3782 .tab_bar
3783 .get_or_insert_default()
3784 .show_tab_bar_buttons = value;
3785 },
3786 }),
3787 metadata: None,
3788 files: USER,
3789 }),
3790 SettingsPageItem::SettingItem(SettingItem {
3791 title: "Pinned Tabs Layout",
3792 description: "Show pinned tabs in a separate row above unpinned tabs.",
3793 field: Box::new(SettingField {
3794 json_path: Some("tab_bar.show_pinned_tabs_in_separate_row"),
3795 pick: |settings_content| {
3796 settings_content
3797 .tab_bar
3798 .as_ref()?
3799 .show_pinned_tabs_in_separate_row
3800 .as_ref()
3801 },
3802 write: |settings_content, value| {
3803 settings_content
3804 .tab_bar
3805 .get_or_insert_default()
3806 .show_pinned_tabs_in_separate_row = value;
3807 },
3808 }),
3809 metadata: None,
3810 files: USER,
3811 }),
3812 ]
3813 }
3814
3815 fn tab_settings_section() -> [SettingsPageItem; 4] {
3816 [
3817 SettingsPageItem::SectionHeader("Tab Settings"),
3818 SettingsPageItem::SettingItem(SettingItem {
3819 title: "Activate On Close",
3820 description: "What to do after closing the current tab.",
3821 field: Box::new(SettingField {
3822 json_path: Some("tabs.activate_on_close"),
3823 pick: |settings_content| {
3824 settings_content.tabs.as_ref()?.activate_on_close.as_ref()
3825 },
3826 write: |settings_content, value| {
3827 settings_content
3828 .tabs
3829 .get_or_insert_default()
3830 .activate_on_close = value;
3831 },
3832 }),
3833 metadata: None,
3834 files: USER,
3835 }),
3836 SettingsPageItem::SettingItem(SettingItem {
3837 title: "Tab Show Diagnostics",
3838 description: "Which files containing diagnostic errors/warnings to mark in the tabs.",
3839 field: Box::new(SettingField {
3840 json_path: Some("tabs.show_diagnostics"),
3841 pick: |settings_content| {
3842 settings_content.tabs.as_ref()?.show_diagnostics.as_ref()
3843 },
3844 write: |settings_content, value| {
3845 settings_content
3846 .tabs
3847 .get_or_insert_default()
3848 .show_diagnostics = value;
3849 },
3850 }),
3851 metadata: None,
3852 files: USER,
3853 }),
3854 SettingsPageItem::SettingItem(SettingItem {
3855 title: "Show Close Button",
3856 description: "Controls the appearance behavior of the tab's close button.",
3857 field: Box::new(SettingField {
3858 json_path: Some("tabs.show_close_button"),
3859 pick: |settings_content| {
3860 settings_content.tabs.as_ref()?.show_close_button.as_ref()
3861 },
3862 write: |settings_content, value| {
3863 settings_content
3864 .tabs
3865 .get_or_insert_default()
3866 .show_close_button = value;
3867 },
3868 }),
3869 metadata: None,
3870 files: USER,
3871 }),
3872 ]
3873 }
3874
3875 fn preview_tabs_section() -> [SettingsPageItem; 8] {
3876 [
3877 SettingsPageItem::SectionHeader("Preview Tabs"),
3878 SettingsPageItem::SettingItem(SettingItem {
3879 title: "Preview Tabs Enabled",
3880 description: "Show opened editors as preview tabs.",
3881 field: Box::new(SettingField {
3882 json_path: Some("preview_tabs.enabled"),
3883 pick: |settings_content| {
3884 settings_content.preview_tabs.as_ref()?.enabled.as_ref()
3885 },
3886 write: |settings_content, value| {
3887 settings_content
3888 .preview_tabs
3889 .get_or_insert_default()
3890 .enabled = value;
3891 },
3892 }),
3893 metadata: None,
3894 files: USER,
3895 }),
3896 SettingsPageItem::SettingItem(SettingItem {
3897 title: "Enable Preview From Project Panel",
3898 description: "Whether to open tabs in preview mode when opened from the project panel with a single click.",
3899 field: Box::new(SettingField {
3900 json_path: Some("preview_tabs.enable_preview_from_project_panel"),
3901 pick: |settings_content| {
3902 settings_content
3903 .preview_tabs
3904 .as_ref()?
3905 .enable_preview_from_project_panel
3906 .as_ref()
3907 },
3908 write: |settings_content, value| {
3909 settings_content
3910 .preview_tabs
3911 .get_or_insert_default()
3912 .enable_preview_from_project_panel = value;
3913 },
3914 }),
3915 metadata: None,
3916 files: USER,
3917 }),
3918 SettingsPageItem::SettingItem(SettingItem {
3919 title: "Enable Preview From File Finder",
3920 description: "Whether to open tabs in preview mode when selected from the file finder.",
3921 field: Box::new(SettingField {
3922 json_path: Some("preview_tabs.enable_preview_from_file_finder"),
3923 pick: |settings_content| {
3924 settings_content
3925 .preview_tabs
3926 .as_ref()?
3927 .enable_preview_from_file_finder
3928 .as_ref()
3929 },
3930 write: |settings_content, value| {
3931 settings_content
3932 .preview_tabs
3933 .get_or_insert_default()
3934 .enable_preview_from_file_finder = value;
3935 },
3936 }),
3937 metadata: None,
3938 files: USER,
3939 }),
3940 SettingsPageItem::SettingItem(SettingItem {
3941 title: "Enable Preview From Multibuffer",
3942 description: "Whether to open tabs in preview mode when opened from a multibuffer.",
3943 field: Box::new(SettingField {
3944 json_path: Some("preview_tabs.enable_preview_from_multibuffer"),
3945 pick: |settings_content| {
3946 settings_content
3947 .preview_tabs
3948 .as_ref()?
3949 .enable_preview_from_multibuffer
3950 .as_ref()
3951 },
3952 write: |settings_content, value| {
3953 settings_content
3954 .preview_tabs
3955 .get_or_insert_default()
3956 .enable_preview_from_multibuffer = value;
3957 },
3958 }),
3959 metadata: None,
3960 files: USER,
3961 }),
3962 SettingsPageItem::SettingItem(SettingItem {
3963 title: "Enable Preview Multibuffer From Code Navigation",
3964 description: "Whether to open tabs in preview mode when code navigation is used to open a multibuffer.",
3965 field: Box::new(SettingField {
3966 json_path: Some("preview_tabs.enable_preview_multibuffer_from_code_navigation"),
3967 pick: |settings_content| {
3968 settings_content
3969 .preview_tabs
3970 .as_ref()?
3971 .enable_preview_multibuffer_from_code_navigation
3972 .as_ref()
3973 },
3974 write: |settings_content, value| {
3975 settings_content
3976 .preview_tabs
3977 .get_or_insert_default()
3978 .enable_preview_multibuffer_from_code_navigation = value;
3979 },
3980 }),
3981 metadata: None,
3982 files: USER,
3983 }),
3984 SettingsPageItem::SettingItem(SettingItem {
3985 title: "Enable Preview File From Code Navigation",
3986 description: "Whether to open tabs in preview mode when code navigation is used to open a single file.",
3987 field: Box::new(SettingField {
3988 json_path: Some("preview_tabs.enable_preview_file_from_code_navigation"),
3989 pick: |settings_content| {
3990 settings_content
3991 .preview_tabs
3992 .as_ref()?
3993 .enable_preview_file_from_code_navigation
3994 .as_ref()
3995 },
3996 write: |settings_content, value| {
3997 settings_content
3998 .preview_tabs
3999 .get_or_insert_default()
4000 .enable_preview_file_from_code_navigation = value;
4001 },
4002 }),
4003 metadata: None,
4004 files: USER,
4005 }),
4006 SettingsPageItem::SettingItem(SettingItem {
4007 title: "Enable Keep Preview On Code Navigation",
4008 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.",
4009 field: Box::new(SettingField {
4010 json_path: Some("preview_tabs.enable_keep_preview_on_code_navigation"),
4011 pick: |settings_content| {
4012 settings_content
4013 .preview_tabs
4014 .as_ref()?
4015 .enable_keep_preview_on_code_navigation
4016 .as_ref()
4017 },
4018 write: |settings_content, value| {
4019 settings_content
4020 .preview_tabs
4021 .get_or_insert_default()
4022 .enable_keep_preview_on_code_navigation = value;
4023 },
4024 }),
4025 metadata: None,
4026 files: USER,
4027 }),
4028 ]
4029 }
4030
4031 fn layout_section() -> [SettingsPageItem; 4] {
4032 [
4033 SettingsPageItem::SectionHeader("Layout"),
4034 SettingsPageItem::SettingItem(SettingItem {
4035 title: "Bottom Dock Layout",
4036 description: "Layout mode for the bottom dock.",
4037 field: Box::new(SettingField {
4038 json_path: Some("bottom_dock_layout"),
4039 pick: |settings_content| settings_content.workspace.bottom_dock_layout.as_ref(),
4040 write: |settings_content, value| {
4041 settings_content.workspace.bottom_dock_layout = value;
4042 },
4043 }),
4044 metadata: None,
4045 files: USER,
4046 }),
4047 SettingsPageItem::SettingItem(SettingItem {
4048 files: USER,
4049 title: "Centered Layout Left Padding",
4050 description: "Left padding for centered layout.",
4051 field: Box::new(SettingField {
4052 json_path: Some("centered_layout.left_padding"),
4053 pick: |settings_content| {
4054 settings_content
4055 .workspace
4056 .centered_layout
4057 .as_ref()?
4058 .left_padding
4059 .as_ref()
4060 },
4061 write: |settings_content, value| {
4062 settings_content
4063 .workspace
4064 .centered_layout
4065 .get_or_insert_default()
4066 .left_padding = value;
4067 },
4068 }),
4069 metadata: None,
4070 }),
4071 SettingsPageItem::SettingItem(SettingItem {
4072 files: USER,
4073 title: "Centered Layout Right Padding",
4074 description: "Right padding for centered layout.",
4075 field: Box::new(SettingField {
4076 json_path: Some("centered_layout.right_padding"),
4077 pick: |settings_content| {
4078 settings_content
4079 .workspace
4080 .centered_layout
4081 .as_ref()?
4082 .right_padding
4083 .as_ref()
4084 },
4085 write: |settings_content, value| {
4086 settings_content
4087 .workspace
4088 .centered_layout
4089 .get_or_insert_default()
4090 .right_padding = value;
4091 },
4092 }),
4093 metadata: None,
4094 }),
4095 ]
4096 }
4097
4098 fn window_section() -> [SettingsPageItem; 3] {
4099 [
4100 SettingsPageItem::SectionHeader("Window"),
4101 // todo(settings_ui): Should we filter by platform.as_ref()?
4102 SettingsPageItem::SettingItem(SettingItem {
4103 title: "Use System Window Tabs",
4104 description: "(macOS only) whether to allow Windows to tab together.",
4105 field: Box::new(SettingField {
4106 json_path: Some("use_system_window_tabs"),
4107 pick: |settings_content| {
4108 settings_content.workspace.use_system_window_tabs.as_ref()
4109 },
4110 write: |settings_content, value| {
4111 settings_content.workspace.use_system_window_tabs = value;
4112 },
4113 }),
4114 metadata: None,
4115 files: USER,
4116 }),
4117 SettingsPageItem::SettingItem(SettingItem {
4118 title: "Window Decorations",
4119 description: "(Linux only) whether Zed or your compositor should draw window decorations.",
4120 field: Box::new(SettingField {
4121 json_path: Some("window_decorations"),
4122 pick: |settings_content| settings_content.workspace.window_decorations.as_ref(),
4123 write: |settings_content, value| {
4124 settings_content.workspace.window_decorations = value;
4125 },
4126 }),
4127 metadata: None,
4128 files: USER,
4129 }),
4130 ]
4131 }
4132
4133 fn pane_modifiers_section() -> [SettingsPageItem; 4] {
4134 [
4135 SettingsPageItem::SectionHeader("Pane Modifiers"),
4136 SettingsPageItem::SettingItem(SettingItem {
4137 title: "Inactive Opacity",
4138 description: "Opacity of inactive panels (0.0 - 1.0).",
4139 field: Box::new(SettingField {
4140 json_path: Some("active_pane_modifiers.inactive_opacity"),
4141 pick: |settings_content| {
4142 settings_content
4143 .workspace
4144 .active_pane_modifiers
4145 .as_ref()?
4146 .inactive_opacity
4147 .as_ref()
4148 },
4149 write: |settings_content, value| {
4150 settings_content
4151 .workspace
4152 .active_pane_modifiers
4153 .get_or_insert_default()
4154 .inactive_opacity = value;
4155 },
4156 }),
4157 metadata: None,
4158 files: USER,
4159 }),
4160 SettingsPageItem::SettingItem(SettingItem {
4161 title: "Border Size",
4162 description: "Size of the border surrounding the active pane.",
4163 field: Box::new(SettingField {
4164 json_path: Some("active_pane_modifiers.border_size"),
4165 pick: |settings_content| {
4166 settings_content
4167 .workspace
4168 .active_pane_modifiers
4169 .as_ref()?
4170 .border_size
4171 .as_ref()
4172 },
4173 write: |settings_content, value| {
4174 settings_content
4175 .workspace
4176 .active_pane_modifiers
4177 .get_or_insert_default()
4178 .border_size = value;
4179 },
4180 }),
4181 metadata: None,
4182 files: USER,
4183 }),
4184 SettingsPageItem::SettingItem(SettingItem {
4185 title: "Zoomed Padding",
4186 description: "Show padding for zoomed panes.",
4187 field: Box::new(SettingField {
4188 json_path: Some("zoomed_padding"),
4189 pick: |settings_content| settings_content.workspace.zoomed_padding.as_ref(),
4190 write: |settings_content, value| {
4191 settings_content.workspace.zoomed_padding = value;
4192 },
4193 }),
4194 metadata: None,
4195 files: USER,
4196 }),
4197 ]
4198 }
4199
4200 fn pane_split_direction_section() -> [SettingsPageItem; 3] {
4201 [
4202 SettingsPageItem::SectionHeader("Pane Split Direction"),
4203 SettingsPageItem::SettingItem(SettingItem {
4204 title: "Vertical Split Direction",
4205 description: "Direction to split vertically.",
4206 field: Box::new(SettingField {
4207 json_path: Some("pane_split_direction_vertical"),
4208 pick: |settings_content| {
4209 settings_content
4210 .workspace
4211 .pane_split_direction_vertical
4212 .as_ref()
4213 },
4214 write: |settings_content, value| {
4215 settings_content.workspace.pane_split_direction_vertical = value;
4216 },
4217 }),
4218 metadata: None,
4219 files: USER,
4220 }),
4221 SettingsPageItem::SettingItem(SettingItem {
4222 title: "Horizontal Split Direction",
4223 description: "Direction to split horizontally.",
4224 field: Box::new(SettingField {
4225 json_path: Some("pane_split_direction_horizontal"),
4226 pick: |settings_content| {
4227 settings_content
4228 .workspace
4229 .pane_split_direction_horizontal
4230 .as_ref()
4231 },
4232 write: |settings_content, value| {
4233 settings_content.workspace.pane_split_direction_horizontal = value;
4234 },
4235 }),
4236 metadata: None,
4237 files: USER,
4238 }),
4239 ]
4240 }
4241
4242 SettingsPage {
4243 title: "Window & Layout",
4244 items: concat_sections![
4245 status_bar_section(),
4246 title_bar_section(),
4247 tab_bar_section(),
4248 tab_settings_section(),
4249 preview_tabs_section(),
4250 layout_section(),
4251 window_section(),
4252 pane_modifiers_section(),
4253 pane_split_direction_section(),
4254 ],
4255 }
4256}
4257
4258fn panels_page() -> SettingsPage {
4259 fn project_panel_section() -> [SettingsPageItem; 21] {
4260 [
4261 SettingsPageItem::SectionHeader("Project Panel"),
4262 SettingsPageItem::SettingItem(SettingItem {
4263 title: "Project Panel Dock",
4264 description: "Where to dock the project panel.",
4265 field: Box::new(SettingField {
4266 json_path: Some("project_panel.dock"),
4267 pick: |settings_content| settings_content.project_panel.as_ref()?.dock.as_ref(),
4268 write: |settings_content, value| {
4269 settings_content.project_panel.get_or_insert_default().dock = value;
4270 },
4271 }),
4272 metadata: None,
4273 files: USER,
4274 }),
4275 SettingsPageItem::SettingItem(SettingItem {
4276 title: "Project Panel Default Width",
4277 description: "Default width of the project panel in pixels.",
4278 field: Box::new(SettingField {
4279 json_path: Some("project_panel.default_width"),
4280 pick: |settings_content| {
4281 settings_content
4282 .project_panel
4283 .as_ref()?
4284 .default_width
4285 .as_ref()
4286 },
4287 write: |settings_content, value| {
4288 settings_content
4289 .project_panel
4290 .get_or_insert_default()
4291 .default_width = value;
4292 },
4293 }),
4294 metadata: None,
4295 files: USER,
4296 }),
4297 SettingsPageItem::SettingItem(SettingItem {
4298 title: "Hide .gitignore",
4299 description: "Whether to hide the gitignore entries in the project panel.",
4300 field: Box::new(SettingField {
4301 json_path: Some("project_panel.hide_gitignore"),
4302 pick: |settings_content| {
4303 settings_content
4304 .project_panel
4305 .as_ref()?
4306 .hide_gitignore
4307 .as_ref()
4308 },
4309 write: |settings_content, value| {
4310 settings_content
4311 .project_panel
4312 .get_or_insert_default()
4313 .hide_gitignore = value;
4314 },
4315 }),
4316 metadata: None,
4317 files: USER,
4318 }),
4319 SettingsPageItem::SettingItem(SettingItem {
4320 title: "Entry Spacing",
4321 description: "Spacing between worktree entries in the project panel.",
4322 field: Box::new(SettingField {
4323 json_path: Some("project_panel.entry_spacing"),
4324 pick: |settings_content| {
4325 settings_content
4326 .project_panel
4327 .as_ref()?
4328 .entry_spacing
4329 .as_ref()
4330 },
4331 write: |settings_content, value| {
4332 settings_content
4333 .project_panel
4334 .get_or_insert_default()
4335 .entry_spacing = value;
4336 },
4337 }),
4338 metadata: None,
4339 files: USER,
4340 }),
4341 SettingsPageItem::SettingItem(SettingItem {
4342 title: "File Icons",
4343 description: "Show file icons in the project panel.",
4344 field: Box::new(SettingField {
4345 json_path: Some("project_panel.file_icons"),
4346 pick: |settings_content| {
4347 settings_content.project_panel.as_ref()?.file_icons.as_ref()
4348 },
4349 write: |settings_content, value| {
4350 settings_content
4351 .project_panel
4352 .get_or_insert_default()
4353 .file_icons = value;
4354 },
4355 }),
4356 metadata: None,
4357 files: USER,
4358 }),
4359 SettingsPageItem::SettingItem(SettingItem {
4360 title: "Folder Icons",
4361 description: "Whether to show folder icons or chevrons for directories in the project panel.",
4362 field: Box::new(SettingField {
4363 json_path: Some("project_panel.folder_icons"),
4364 pick: |settings_content| {
4365 settings_content
4366 .project_panel
4367 .as_ref()?
4368 .folder_icons
4369 .as_ref()
4370 },
4371 write: |settings_content, value| {
4372 settings_content
4373 .project_panel
4374 .get_or_insert_default()
4375 .folder_icons = value;
4376 },
4377 }),
4378 metadata: None,
4379 files: USER,
4380 }),
4381 SettingsPageItem::SettingItem(SettingItem {
4382 title: "Git Status",
4383 description: "Show the Git status in the project panel.",
4384 field: Box::new(SettingField {
4385 json_path: Some("project_panel.git_status"),
4386 pick: |settings_content| {
4387 settings_content.project_panel.as_ref()?.git_status.as_ref()
4388 },
4389 write: |settings_content, value| {
4390 settings_content
4391 .project_panel
4392 .get_or_insert_default()
4393 .git_status = value;
4394 },
4395 }),
4396 metadata: None,
4397 files: USER,
4398 }),
4399 SettingsPageItem::SettingItem(SettingItem {
4400 title: "Indent Size",
4401 description: "Amount of indentation for nested items.",
4402 field: Box::new(SettingField {
4403 json_path: Some("project_panel.indent_size"),
4404 pick: |settings_content| {
4405 settings_content
4406 .project_panel
4407 .as_ref()?
4408 .indent_size
4409 .as_ref()
4410 },
4411 write: |settings_content, value| {
4412 settings_content
4413 .project_panel
4414 .get_or_insert_default()
4415 .indent_size = value;
4416 },
4417 }),
4418 metadata: None,
4419 files: USER,
4420 }),
4421 SettingsPageItem::SettingItem(SettingItem {
4422 title: "Auto Reveal Entries",
4423 description: "Whether to reveal entries in the project panel automatically when a corresponding project entry becomes active.",
4424 field: Box::new(SettingField {
4425 json_path: Some("project_panel.auto_reveal_entries"),
4426 pick: |settings_content| {
4427 settings_content
4428 .project_panel
4429 .as_ref()?
4430 .auto_reveal_entries
4431 .as_ref()
4432 },
4433 write: |settings_content, value| {
4434 settings_content
4435 .project_panel
4436 .get_or_insert_default()
4437 .auto_reveal_entries = value;
4438 },
4439 }),
4440 metadata: None,
4441 files: USER,
4442 }),
4443 SettingsPageItem::SettingItem(SettingItem {
4444 title: "Starts Open",
4445 description: "Whether the project panel should open on startup.",
4446 field: Box::new(SettingField {
4447 json_path: Some("project_panel.starts_open"),
4448 pick: |settings_content| {
4449 settings_content
4450 .project_panel
4451 .as_ref()?
4452 .starts_open
4453 .as_ref()
4454 },
4455 write: |settings_content, value| {
4456 settings_content
4457 .project_panel
4458 .get_or_insert_default()
4459 .starts_open = value;
4460 },
4461 }),
4462 metadata: None,
4463 files: USER,
4464 }),
4465 SettingsPageItem::SettingItem(SettingItem {
4466 title: "Auto Fold Directories",
4467 description: "Whether to fold directories automatically and show compact folders when a directory has only one subdirectory inside.",
4468 field: Box::new(SettingField {
4469 json_path: Some("project_panel.auto_fold_dirs"),
4470 pick: |settings_content| {
4471 settings_content
4472 .project_panel
4473 .as_ref()?
4474 .auto_fold_dirs
4475 .as_ref()
4476 },
4477 write: |settings_content, value| {
4478 settings_content
4479 .project_panel
4480 .get_or_insert_default()
4481 .auto_fold_dirs = value;
4482 },
4483 }),
4484 metadata: None,
4485 files: USER,
4486 }),
4487 SettingsPageItem::SettingItem(SettingItem {
4488 title: "Bold Folder Labels",
4489 description: "Whether to show folder names with bold text in the project panel.",
4490 field: Box::new(SettingField {
4491 json_path: Some("project_panel.bold_folder_labels"),
4492 pick: |settings_content| {
4493 settings_content
4494 .project_panel
4495 .as_ref()?
4496 .bold_folder_labels
4497 .as_ref()
4498 },
4499 write: |settings_content, value| {
4500 settings_content
4501 .project_panel
4502 .get_or_insert_default()
4503 .bold_folder_labels = value;
4504 },
4505 }),
4506 metadata: None,
4507 files: USER,
4508 }),
4509 SettingsPageItem::SettingItem(SettingItem {
4510 title: "Show Scrollbar",
4511 description: "Show the scrollbar in the project panel.",
4512 field: Box::new(SettingField {
4513 json_path: Some("project_panel.scrollbar.show"),
4514 pick: |settings_content| {
4515 show_scrollbar_or_editor(settings_content, |settings_content| {
4516 settings_content
4517 .project_panel
4518 .as_ref()?
4519 .scrollbar
4520 .as_ref()?
4521 .show
4522 .as_ref()
4523 })
4524 },
4525 write: |settings_content, value| {
4526 settings_content
4527 .project_panel
4528 .get_or_insert_default()
4529 .scrollbar
4530 .get_or_insert_default()
4531 .show = value;
4532 },
4533 }),
4534 metadata: None,
4535 files: USER,
4536 }),
4537 SettingsPageItem::SettingItem(SettingItem {
4538 title: "Show Diagnostics",
4539 description: "Which files containing diagnostic errors/warnings to mark in the project panel.",
4540 field: Box::new(SettingField {
4541 json_path: Some("project_panel.show_diagnostics"),
4542 pick: |settings_content| {
4543 settings_content
4544 .project_panel
4545 .as_ref()?
4546 .show_diagnostics
4547 .as_ref()
4548 },
4549 write: |settings_content, value| {
4550 settings_content
4551 .project_panel
4552 .get_or_insert_default()
4553 .show_diagnostics = value;
4554 },
4555 }),
4556 metadata: None,
4557 files: USER,
4558 }),
4559 SettingsPageItem::SettingItem(SettingItem {
4560 title: "Sticky Scroll",
4561 description: "Whether to stick parent directories at top of the project panel.",
4562 field: Box::new(SettingField {
4563 json_path: Some("project_panel.sticky_scroll"),
4564 pick: |settings_content| {
4565 settings_content
4566 .project_panel
4567 .as_ref()?
4568 .sticky_scroll
4569 .as_ref()
4570 },
4571 write: |settings_content, value| {
4572 settings_content
4573 .project_panel
4574 .get_or_insert_default()
4575 .sticky_scroll = value;
4576 },
4577 }),
4578 metadata: None,
4579 files: USER,
4580 }),
4581 SettingsPageItem::SettingItem(SettingItem {
4582 files: USER,
4583 title: "Show Indent Guides",
4584 description: "Show indent guides in the project panel.",
4585 field: Box::new(SettingField {
4586 json_path: Some("project_panel.indent_guides.show"),
4587 pick: |settings_content| {
4588 settings_content
4589 .project_panel
4590 .as_ref()?
4591 .indent_guides
4592 .as_ref()?
4593 .show
4594 .as_ref()
4595 },
4596 write: |settings_content, value| {
4597 settings_content
4598 .project_panel
4599 .get_or_insert_default()
4600 .indent_guides
4601 .get_or_insert_default()
4602 .show = value;
4603 },
4604 }),
4605 metadata: None,
4606 }),
4607 SettingsPageItem::SettingItem(SettingItem {
4608 title: "Drag and Drop",
4609 description: "Whether to enable drag-and-drop operations in the project panel.",
4610 field: Box::new(SettingField {
4611 json_path: Some("project_panel.drag_and_drop"),
4612 pick: |settings_content| {
4613 settings_content
4614 .project_panel
4615 .as_ref()?
4616 .drag_and_drop
4617 .as_ref()
4618 },
4619 write: |settings_content, value| {
4620 settings_content
4621 .project_panel
4622 .get_or_insert_default()
4623 .drag_and_drop = value;
4624 },
4625 }),
4626 metadata: None,
4627 files: USER,
4628 }),
4629 SettingsPageItem::SettingItem(SettingItem {
4630 title: "Hide Root",
4631 description: "Whether to hide the root entry when only one folder is open in the window.",
4632 field: Box::new(SettingField {
4633 json_path: Some("project_panel.drag_and_drop"),
4634 pick: |settings_content| {
4635 settings_content.project_panel.as_ref()?.hide_root.as_ref()
4636 },
4637 write: |settings_content, value| {
4638 settings_content
4639 .project_panel
4640 .get_or_insert_default()
4641 .hide_root = value;
4642 },
4643 }),
4644 metadata: None,
4645 files: USER,
4646 }),
4647 SettingsPageItem::SettingItem(SettingItem {
4648 title: "Hide Hidden",
4649 description: "Whether to hide the hidden entries in the project panel.",
4650 field: Box::new(SettingField {
4651 json_path: Some("project_panel.hide_hidden"),
4652 pick: |settings_content| {
4653 settings_content
4654 .project_panel
4655 .as_ref()?
4656 .hide_hidden
4657 .as_ref()
4658 },
4659 write: |settings_content, value| {
4660 settings_content
4661 .project_panel
4662 .get_or_insert_default()
4663 .hide_hidden = value;
4664 },
4665 }),
4666 metadata: None,
4667 files: USER,
4668 }),
4669 SettingsPageItem::SettingItem(SettingItem {
4670 title: "Hidden Files",
4671 description: "Globs to match files that will be considered \"hidden\" and can be hidden from the project panel.",
4672 field: Box::new(
4673 SettingField {
4674 json_path: Some("worktree.hidden_files"),
4675 pick: |settings_content| {
4676 settings_content.project.worktree.hidden_files.as_ref()
4677 },
4678 write: |settings_content, value| {
4679 settings_content.project.worktree.hidden_files = value;
4680 },
4681 }
4682 .unimplemented(),
4683 ),
4684 metadata: None,
4685 files: USER,
4686 }),
4687 ]
4688 }
4689
4690 fn auto_open_files_section() -> [SettingsPageItem; 5] {
4691 [
4692 SettingsPageItem::SectionHeader("Auto Open Files"),
4693 SettingsPageItem::SettingItem(SettingItem {
4694 title: "On Create",
4695 description: "Whether to automatically open newly created files in the editor.",
4696 field: Box::new(SettingField {
4697 json_path: Some("project_panel.auto_open.on_create"),
4698 pick: |settings_content| {
4699 settings_content
4700 .project_panel
4701 .as_ref()?
4702 .auto_open
4703 .as_ref()?
4704 .on_create
4705 .as_ref()
4706 },
4707 write: |settings_content, value| {
4708 settings_content
4709 .project_panel
4710 .get_or_insert_default()
4711 .auto_open
4712 .get_or_insert_default()
4713 .on_create = value;
4714 },
4715 }),
4716 metadata: None,
4717 files: USER,
4718 }),
4719 SettingsPageItem::SettingItem(SettingItem {
4720 title: "On Paste",
4721 description: "Whether to automatically open files after pasting or duplicating them.",
4722 field: Box::new(SettingField {
4723 json_path: Some("project_panel.auto_open.on_paste"),
4724 pick: |settings_content| {
4725 settings_content
4726 .project_panel
4727 .as_ref()?
4728 .auto_open
4729 .as_ref()?
4730 .on_paste
4731 .as_ref()
4732 },
4733 write: |settings_content, value| {
4734 settings_content
4735 .project_panel
4736 .get_or_insert_default()
4737 .auto_open
4738 .get_or_insert_default()
4739 .on_paste = value;
4740 },
4741 }),
4742 metadata: None,
4743 files: USER,
4744 }),
4745 SettingsPageItem::SettingItem(SettingItem {
4746 title: "On Drop",
4747 description: "Whether to automatically open files dropped from external sources.",
4748 field: Box::new(SettingField {
4749 json_path: Some("project_panel.auto_open.on_drop"),
4750 pick: |settings_content| {
4751 settings_content
4752 .project_panel
4753 .as_ref()?
4754 .auto_open
4755 .as_ref()?
4756 .on_drop
4757 .as_ref()
4758 },
4759 write: |settings_content, value| {
4760 settings_content
4761 .project_panel
4762 .get_or_insert_default()
4763 .auto_open
4764 .get_or_insert_default()
4765 .on_drop = value;
4766 },
4767 }),
4768 metadata: None,
4769 files: USER,
4770 }),
4771 SettingsPageItem::SettingItem(SettingItem {
4772 title: "Sort Mode",
4773 description: "Sort order for entries in the project panel.",
4774 field: Box::new(SettingField {
4775 pick: |settings_content| {
4776 settings_content.project_panel.as_ref()?.sort_mode.as_ref()
4777 },
4778 write: |settings_content, value| {
4779 settings_content
4780 .project_panel
4781 .get_or_insert_default()
4782 .sort_mode = value;
4783 },
4784 json_path: Some("project_panel.sort_mode"),
4785 }),
4786 metadata: None,
4787 files: USER,
4788 }),
4789 ]
4790 }
4791
4792 fn terminal_panel_section() -> [SettingsPageItem; 2] {
4793 [
4794 SettingsPageItem::SectionHeader("Terminal Panel"),
4795 SettingsPageItem::SettingItem(SettingItem {
4796 title: "Terminal Dock",
4797 description: "Where to dock the terminal panel.",
4798 field: Box::new(SettingField {
4799 json_path: Some("terminal.dock"),
4800 pick: |settings_content| settings_content.terminal.as_ref()?.dock.as_ref(),
4801 write: |settings_content, value| {
4802 settings_content.terminal.get_or_insert_default().dock = value;
4803 },
4804 }),
4805 metadata: None,
4806 files: USER,
4807 }),
4808 ]
4809 }
4810
4811 fn outline_panel_section() -> [SettingsPageItem; 11] {
4812 [
4813 SettingsPageItem::SectionHeader("Outline Panel"),
4814 SettingsPageItem::SettingItem(SettingItem {
4815 title: "Outline Panel Button",
4816 description: "Show the outline panel button in the status bar.",
4817 field: Box::new(SettingField {
4818 json_path: Some("outline_panel.button"),
4819 pick: |settings_content| {
4820 settings_content.outline_panel.as_ref()?.button.as_ref()
4821 },
4822 write: |settings_content, value| {
4823 settings_content
4824 .outline_panel
4825 .get_or_insert_default()
4826 .button = value;
4827 },
4828 }),
4829 metadata: None,
4830 files: USER,
4831 }),
4832 SettingsPageItem::SettingItem(SettingItem {
4833 title: "Outline Panel Dock",
4834 description: "Where to dock the outline panel.",
4835 field: Box::new(SettingField {
4836 json_path: Some("outline_panel.dock"),
4837 pick: |settings_content| settings_content.outline_panel.as_ref()?.dock.as_ref(),
4838 write: |settings_content, value| {
4839 settings_content.outline_panel.get_or_insert_default().dock = value;
4840 },
4841 }),
4842 metadata: None,
4843 files: USER,
4844 }),
4845 SettingsPageItem::SettingItem(SettingItem {
4846 title: "Outline Panel Default Width",
4847 description: "Default width of the outline panel in pixels.",
4848 field: Box::new(SettingField {
4849 json_path: Some("outline_panel.default_width"),
4850 pick: |settings_content| {
4851 settings_content
4852 .outline_panel
4853 .as_ref()?
4854 .default_width
4855 .as_ref()
4856 },
4857 write: |settings_content, value| {
4858 settings_content
4859 .outline_panel
4860 .get_or_insert_default()
4861 .default_width = value;
4862 },
4863 }),
4864 metadata: None,
4865 files: USER,
4866 }),
4867 SettingsPageItem::SettingItem(SettingItem {
4868 title: "File Icons",
4869 description: "Show file icons in the outline panel.",
4870 field: Box::new(SettingField {
4871 json_path: Some("outline_panel.file_icons"),
4872 pick: |settings_content| {
4873 settings_content.outline_panel.as_ref()?.file_icons.as_ref()
4874 },
4875 write: |settings_content, value| {
4876 settings_content
4877 .outline_panel
4878 .get_or_insert_default()
4879 .file_icons = value;
4880 },
4881 }),
4882 metadata: None,
4883 files: USER,
4884 }),
4885 SettingsPageItem::SettingItem(SettingItem {
4886 title: "Folder Icons",
4887 description: "Whether to show folder icons or chevrons for directories in the outline panel.",
4888 field: Box::new(SettingField {
4889 json_path: Some("outline_panel.folder_icons"),
4890 pick: |settings_content| {
4891 settings_content
4892 .outline_panel
4893 .as_ref()?
4894 .folder_icons
4895 .as_ref()
4896 },
4897 write: |settings_content, value| {
4898 settings_content
4899 .outline_panel
4900 .get_or_insert_default()
4901 .folder_icons = value;
4902 },
4903 }),
4904 metadata: None,
4905 files: USER,
4906 }),
4907 SettingsPageItem::SettingItem(SettingItem {
4908 title: "Git Status",
4909 description: "Show the Git status in the outline panel.",
4910 field: Box::new(SettingField {
4911 json_path: Some("outline_panel.git_status"),
4912 pick: |settings_content| {
4913 settings_content.outline_panel.as_ref()?.git_status.as_ref()
4914 },
4915 write: |settings_content, value| {
4916 settings_content
4917 .outline_panel
4918 .get_or_insert_default()
4919 .git_status = value;
4920 },
4921 }),
4922 metadata: None,
4923 files: USER,
4924 }),
4925 SettingsPageItem::SettingItem(SettingItem {
4926 title: "Indent Size",
4927 description: "Amount of indentation for nested items.",
4928 field: Box::new(SettingField {
4929 json_path: Some("outline_panel.indent_size"),
4930 pick: |settings_content| {
4931 settings_content
4932 .outline_panel
4933 .as_ref()?
4934 .indent_size
4935 .as_ref()
4936 },
4937 write: |settings_content, value| {
4938 settings_content
4939 .outline_panel
4940 .get_or_insert_default()
4941 .indent_size = value;
4942 },
4943 }),
4944 metadata: None,
4945 files: USER,
4946 }),
4947 SettingsPageItem::SettingItem(SettingItem {
4948 title: "Auto Reveal Entries",
4949 description: "Whether to reveal when a corresponding outline entry becomes active.",
4950 field: Box::new(SettingField {
4951 json_path: Some("outline_panel.auto_reveal_entries"),
4952 pick: |settings_content| {
4953 settings_content
4954 .outline_panel
4955 .as_ref()?
4956 .auto_reveal_entries
4957 .as_ref()
4958 },
4959 write: |settings_content, value| {
4960 settings_content
4961 .outline_panel
4962 .get_or_insert_default()
4963 .auto_reveal_entries = value;
4964 },
4965 }),
4966 metadata: None,
4967 files: USER,
4968 }),
4969 SettingsPageItem::SettingItem(SettingItem {
4970 title: "Auto Fold Directories",
4971 description: "Whether to fold directories automatically when a directory contains only one subdirectory.",
4972 field: Box::new(SettingField {
4973 json_path: Some("outline_panel.auto_fold_dirs"),
4974 pick: |settings_content| {
4975 settings_content
4976 .outline_panel
4977 .as_ref()?
4978 .auto_fold_dirs
4979 .as_ref()
4980 },
4981 write: |settings_content, value| {
4982 settings_content
4983 .outline_panel
4984 .get_or_insert_default()
4985 .auto_fold_dirs = value;
4986 },
4987 }),
4988 metadata: None,
4989 files: USER,
4990 }),
4991 SettingsPageItem::SettingItem(SettingItem {
4992 files: USER,
4993 title: "Show Indent Guides",
4994 description: "When to show indent guides in the outline panel.",
4995 field: Box::new(SettingField {
4996 json_path: Some("outline_panel.indent_guides.show"),
4997 pick: |settings_content| {
4998 settings_content
4999 .outline_panel
5000 .as_ref()?
5001 .indent_guides
5002 .as_ref()?
5003 .show
5004 .as_ref()
5005 },
5006 write: |settings_content, value| {
5007 settings_content
5008 .outline_panel
5009 .get_or_insert_default()
5010 .indent_guides
5011 .get_or_insert_default()
5012 .show = value;
5013 },
5014 }),
5015 metadata: None,
5016 }),
5017 ]
5018 }
5019
5020 fn git_panel_section() -> [SettingsPageItem; 10] {
5021 [
5022 SettingsPageItem::SectionHeader("Git Panel"),
5023 SettingsPageItem::SettingItem(SettingItem {
5024 title: "Git Panel Button",
5025 description: "Show the Git panel button in the status bar.",
5026 field: Box::new(SettingField {
5027 json_path: Some("git_panel.button"),
5028 pick: |settings_content| settings_content.git_panel.as_ref()?.button.as_ref(),
5029 write: |settings_content, value| {
5030 settings_content.git_panel.get_or_insert_default().button = value;
5031 },
5032 }),
5033 metadata: None,
5034 files: USER,
5035 }),
5036 SettingsPageItem::SettingItem(SettingItem {
5037 title: "Git Panel Dock",
5038 description: "Where to dock the Git panel.",
5039 field: Box::new(SettingField {
5040 json_path: Some("git_panel.dock"),
5041 pick: |settings_content| settings_content.git_panel.as_ref()?.dock.as_ref(),
5042 write: |settings_content, value| {
5043 settings_content.git_panel.get_or_insert_default().dock = value;
5044 },
5045 }),
5046 metadata: None,
5047 files: USER,
5048 }),
5049 SettingsPageItem::SettingItem(SettingItem {
5050 title: "Git Panel Default Width",
5051 description: "Default width of the Git panel in pixels.",
5052 field: Box::new(SettingField {
5053 json_path: Some("git_panel.default_width"),
5054 pick: |settings_content| {
5055 settings_content.git_panel.as_ref()?.default_width.as_ref()
5056 },
5057 write: |settings_content, value| {
5058 settings_content
5059 .git_panel
5060 .get_or_insert_default()
5061 .default_width = value;
5062 },
5063 }),
5064 metadata: None,
5065 files: USER,
5066 }),
5067 SettingsPageItem::SettingItem(SettingItem {
5068 title: "Git Panel Status Style",
5069 description: "How entry statuses are displayed.",
5070 field: Box::new(SettingField {
5071 json_path: Some("git_panel.status_style"),
5072 pick: |settings_content| {
5073 settings_content.git_panel.as_ref()?.status_style.as_ref()
5074 },
5075 write: |settings_content, value| {
5076 settings_content
5077 .git_panel
5078 .get_or_insert_default()
5079 .status_style = value;
5080 },
5081 }),
5082 metadata: None,
5083 files: USER,
5084 }),
5085 SettingsPageItem::SettingItem(SettingItem {
5086 title: "Fallback Branch Name",
5087 description: "Default branch name will be when init.defaultbranch is not set in Git.",
5088 field: Box::new(SettingField {
5089 json_path: Some("git_panel.fallback_branch_name"),
5090 pick: |settings_content| {
5091 settings_content
5092 .git_panel
5093 .as_ref()?
5094 .fallback_branch_name
5095 .as_ref()
5096 },
5097 write: |settings_content, value| {
5098 settings_content
5099 .git_panel
5100 .get_or_insert_default()
5101 .fallback_branch_name = value;
5102 },
5103 }),
5104 metadata: None,
5105 files: USER,
5106 }),
5107 SettingsPageItem::SettingItem(SettingItem {
5108 title: "Sort By Path",
5109 description: "Enable to sort entries in the panel by path, disable to sort by status.",
5110 field: Box::new(SettingField {
5111 json_path: Some("git_panel.sort_by_path"),
5112 pick: |settings_content| {
5113 settings_content.git_panel.as_ref()?.sort_by_path.as_ref()
5114 },
5115 write: |settings_content, value| {
5116 settings_content
5117 .git_panel
5118 .get_or_insert_default()
5119 .sort_by_path = value;
5120 },
5121 }),
5122 metadata: None,
5123 files: USER,
5124 }),
5125 SettingsPageItem::SettingItem(SettingItem {
5126 title: "Collapse Untracked Diff",
5127 description: "Whether to collapse untracked files in the diff panel.",
5128 field: Box::new(SettingField {
5129 json_path: Some("git_panel.collapse_untracked_diff"),
5130 pick: |settings_content| {
5131 settings_content
5132 .git_panel
5133 .as_ref()?
5134 .collapse_untracked_diff
5135 .as_ref()
5136 },
5137 write: |settings_content, value| {
5138 settings_content
5139 .git_panel
5140 .get_or_insert_default()
5141 .collapse_untracked_diff = value;
5142 },
5143 }),
5144 metadata: None,
5145 files: USER,
5146 }),
5147 SettingsPageItem::SettingItem(SettingItem {
5148 title: "Tree View",
5149 description: "Enable to show entries in tree view list, disable to show in flat view list.",
5150 field: Box::new(SettingField {
5151 json_path: Some("git_panel.tree_view"),
5152 pick: |settings_content| {
5153 settings_content.git_panel.as_ref()?.tree_view.as_ref()
5154 },
5155 write: |settings_content, value| {
5156 settings_content.git_panel.get_or_insert_default().tree_view = value;
5157 },
5158 }),
5159 metadata: None,
5160 files: USER,
5161 }),
5162 SettingsPageItem::SettingItem(SettingItem {
5163 title: "Scroll Bar",
5164 description: "How and when the scrollbar should be displayed.",
5165 field: Box::new(SettingField {
5166 json_path: Some("git_panel.scrollbar.show"),
5167 pick: |settings_content| {
5168 show_scrollbar_or_editor(settings_content, |settings_content| {
5169 settings_content
5170 .git_panel
5171 .as_ref()?
5172 .scrollbar
5173 .as_ref()?
5174 .show
5175 .as_ref()
5176 })
5177 },
5178 write: |settings_content, value| {
5179 settings_content
5180 .git_panel
5181 .get_or_insert_default()
5182 .scrollbar
5183 .get_or_insert_default()
5184 .show = value;
5185 },
5186 }),
5187 metadata: None,
5188 files: USER,
5189 }),
5190 ]
5191 }
5192
5193 fn debugger_panel_section() -> [SettingsPageItem; 2] {
5194 [
5195 SettingsPageItem::SectionHeader("Debugger Panel"),
5196 SettingsPageItem::SettingItem(SettingItem {
5197 title: "Debugger Panel Dock",
5198 description: "The dock position of the debug panel.",
5199 field: Box::new(SettingField {
5200 json_path: Some("debugger.dock"),
5201 pick: |settings_content| settings_content.debugger.as_ref()?.dock.as_ref(),
5202 write: |settings_content, value| {
5203 settings_content.debugger.get_or_insert_default().dock = value;
5204 },
5205 }),
5206 metadata: None,
5207 files: USER,
5208 }),
5209 ]
5210 }
5211
5212 fn notification_panel_section() -> [SettingsPageItem; 4] {
5213 [
5214 SettingsPageItem::SectionHeader("Notification Panel"),
5215 SettingsPageItem::SettingItem(SettingItem {
5216 title: "Notification Panel Button",
5217 description: "Show the notification panel button in the status bar.",
5218 field: Box::new(SettingField {
5219 json_path: Some("notification_panel.button"),
5220 pick: |settings_content| {
5221 settings_content
5222 .notification_panel
5223 .as_ref()?
5224 .button
5225 .as_ref()
5226 },
5227 write: |settings_content, value| {
5228 settings_content
5229 .notification_panel
5230 .get_or_insert_default()
5231 .button = value;
5232 },
5233 }),
5234 metadata: None,
5235 files: USER,
5236 }),
5237 SettingsPageItem::SettingItem(SettingItem {
5238 title: "Notification Panel Dock",
5239 description: "Where to dock the notification panel.",
5240 field: Box::new(SettingField {
5241 json_path: Some("notification_panel.dock"),
5242 pick: |settings_content| {
5243 settings_content.notification_panel.as_ref()?.dock.as_ref()
5244 },
5245 write: |settings_content, value| {
5246 settings_content
5247 .notification_panel
5248 .get_or_insert_default()
5249 .dock = value;
5250 },
5251 }),
5252 metadata: None,
5253 files: USER,
5254 }),
5255 SettingsPageItem::SettingItem(SettingItem {
5256 title: "Notification Panel Default Width",
5257 description: "Default width of the notification panel in pixels.",
5258 field: Box::new(SettingField {
5259 json_path: Some("notification_panel.default_width"),
5260 pick: |settings_content| {
5261 settings_content
5262 .notification_panel
5263 .as_ref()?
5264 .default_width
5265 .as_ref()
5266 },
5267 write: |settings_content, value| {
5268 settings_content
5269 .notification_panel
5270 .get_or_insert_default()
5271 .default_width = value;
5272 },
5273 }),
5274 metadata: None,
5275 files: USER,
5276 }),
5277 ]
5278 }
5279
5280 fn collaboration_panel_section() -> [SettingsPageItem; 4] {
5281 [
5282 SettingsPageItem::SectionHeader("Collaboration Panel"),
5283 SettingsPageItem::SettingItem(SettingItem {
5284 title: "Collaboration Panel Button",
5285 description: "Show the collaboration panel button in the status bar.",
5286 field: Box::new(SettingField {
5287 json_path: Some("collaboration_panel.button"),
5288 pick: |settings_content| {
5289 settings_content
5290 .collaboration_panel
5291 .as_ref()?
5292 .button
5293 .as_ref()
5294 },
5295 write: |settings_content, value| {
5296 settings_content
5297 .collaboration_panel
5298 .get_or_insert_default()
5299 .button = value;
5300 },
5301 }),
5302 metadata: None,
5303 files: USER,
5304 }),
5305 SettingsPageItem::SettingItem(SettingItem {
5306 title: "Collaboration Panel Dock",
5307 description: "Where to dock the collaboration panel.",
5308 field: Box::new(SettingField {
5309 json_path: Some("collaboration_panel.dock"),
5310 pick: |settings_content| {
5311 settings_content.collaboration_panel.as_ref()?.dock.as_ref()
5312 },
5313 write: |settings_content, value| {
5314 settings_content
5315 .collaboration_panel
5316 .get_or_insert_default()
5317 .dock = value;
5318 },
5319 }),
5320 metadata: None,
5321 files: USER,
5322 }),
5323 SettingsPageItem::SettingItem(SettingItem {
5324 title: "Collaboration Panel Default Width",
5325 description: "Default width of the collaboration panel in pixels.",
5326 field: Box::new(SettingField {
5327 json_path: Some("collaboration_panel.dock"),
5328 pick: |settings_content| {
5329 settings_content
5330 .collaboration_panel
5331 .as_ref()?
5332 .default_width
5333 .as_ref()
5334 },
5335 write: |settings_content, value| {
5336 settings_content
5337 .collaboration_panel
5338 .get_or_insert_default()
5339 .default_width = value;
5340 },
5341 }),
5342 metadata: None,
5343 files: USER,
5344 }),
5345 ]
5346 }
5347
5348 fn agent_panel_section() -> [SettingsPageItem; 5] {
5349 [
5350 SettingsPageItem::SectionHeader("Agent Panel"),
5351 SettingsPageItem::SettingItem(SettingItem {
5352 title: "Agent Panel Button",
5353 description: "Whether to show the agent panel button in the status bar.",
5354 field: Box::new(SettingField {
5355 json_path: Some("agent.button"),
5356 pick: |settings_content| settings_content.agent.as_ref()?.button.as_ref(),
5357 write: |settings_content, value| {
5358 settings_content.agent.get_or_insert_default().button = value;
5359 },
5360 }),
5361 metadata: None,
5362 files: USER,
5363 }),
5364 SettingsPageItem::SettingItem(SettingItem {
5365 title: "Agent Panel Dock",
5366 description: "Where to dock the agent panel.",
5367 field: Box::new(SettingField {
5368 json_path: Some("agent.dock"),
5369 pick: |settings_content| settings_content.agent.as_ref()?.dock.as_ref(),
5370 write: |settings_content, value| {
5371 settings_content.agent.get_or_insert_default().dock = value;
5372 },
5373 }),
5374 metadata: None,
5375 files: USER,
5376 }),
5377 SettingsPageItem::SettingItem(SettingItem {
5378 title: "Agent Panel Default Width",
5379 description: "Default width when the agent panel is docked to the left or right.",
5380 field: Box::new(SettingField {
5381 json_path: Some("agent.default_width"),
5382 pick: |settings_content| {
5383 settings_content.agent.as_ref()?.default_width.as_ref()
5384 },
5385 write: |settings_content, value| {
5386 settings_content.agent.get_or_insert_default().default_width = value;
5387 },
5388 }),
5389 metadata: None,
5390 files: USER,
5391 }),
5392 SettingsPageItem::SettingItem(SettingItem {
5393 title: "Agent Panel Default Height",
5394 description: "Default height when the agent panel is docked to the bottom.",
5395 field: Box::new(SettingField {
5396 json_path: Some("agent.default_height"),
5397 pick: |settings_content| {
5398 settings_content.agent.as_ref()?.default_height.as_ref()
5399 },
5400 write: |settings_content, value| {
5401 settings_content
5402 .agent
5403 .get_or_insert_default()
5404 .default_height = value;
5405 },
5406 }),
5407 metadata: None,
5408 files: USER,
5409 }),
5410 ]
5411 }
5412
5413 SettingsPage {
5414 title: "Panels",
5415 items: concat_sections![
5416 project_panel_section(),
5417 auto_open_files_section(),
5418 terminal_panel_section(),
5419 outline_panel_section(),
5420 git_panel_section(),
5421 debugger_panel_section(),
5422 notification_panel_section(),
5423 collaboration_panel_section(),
5424 agent_panel_section(),
5425 ],
5426 }
5427}
5428
5429fn debugger_page() -> SettingsPage {
5430 fn general_section() -> [SettingsPageItem; 6] {
5431 [
5432 SettingsPageItem::SectionHeader("General"),
5433 SettingsPageItem::SettingItem(SettingItem {
5434 title: "Stepping Granularity",
5435 description: "Determines the stepping granularity for debug operations.",
5436 field: Box::new(SettingField {
5437 json_path: Some("debugger.stepping_granularity"),
5438 pick: |settings_content| {
5439 settings_content
5440 .debugger
5441 .as_ref()?
5442 .stepping_granularity
5443 .as_ref()
5444 },
5445 write: |settings_content, value| {
5446 settings_content
5447 .debugger
5448 .get_or_insert_default()
5449 .stepping_granularity = value;
5450 },
5451 }),
5452 metadata: None,
5453 files: USER,
5454 }),
5455 SettingsPageItem::SettingItem(SettingItem {
5456 title: "Save Breakpoints",
5457 description: "Whether breakpoints should be reused across Zed sessions.",
5458 field: Box::new(SettingField {
5459 json_path: Some("debugger.save_breakpoints"),
5460 pick: |settings_content| {
5461 settings_content
5462 .debugger
5463 .as_ref()?
5464 .save_breakpoints
5465 .as_ref()
5466 },
5467 write: |settings_content, value| {
5468 settings_content
5469 .debugger
5470 .get_or_insert_default()
5471 .save_breakpoints = value;
5472 },
5473 }),
5474 metadata: None,
5475 files: USER,
5476 }),
5477 SettingsPageItem::SettingItem(SettingItem {
5478 title: "Timeout",
5479 description: "Time in milliseconds until timeout error when connecting to a TCP debug adapter.",
5480 field: Box::new(SettingField {
5481 json_path: Some("debugger.timeout"),
5482 pick: |settings_content| settings_content.debugger.as_ref()?.timeout.as_ref(),
5483 write: |settings_content, value| {
5484 settings_content.debugger.get_or_insert_default().timeout = value;
5485 },
5486 }),
5487 metadata: None,
5488 files: USER,
5489 }),
5490 SettingsPageItem::SettingItem(SettingItem {
5491 title: "Log DAP Communications",
5492 description: "Whether to log messages between active debug adapters and Zed.",
5493 field: Box::new(SettingField {
5494 json_path: Some("debugger.log_dap_communications"),
5495 pick: |settings_content| {
5496 settings_content
5497 .debugger
5498 .as_ref()?
5499 .log_dap_communications
5500 .as_ref()
5501 },
5502 write: |settings_content, value| {
5503 settings_content
5504 .debugger
5505 .get_or_insert_default()
5506 .log_dap_communications = value;
5507 },
5508 }),
5509 metadata: None,
5510 files: USER,
5511 }),
5512 SettingsPageItem::SettingItem(SettingItem {
5513 title: "Format DAP Log Messages",
5514 description: "Whether to format DAP messages when adding them to debug adapter logger.",
5515 field: Box::new(SettingField {
5516 json_path: Some("debugger.format_dap_log_messages"),
5517 pick: |settings_content| {
5518 settings_content
5519 .debugger
5520 .as_ref()?
5521 .format_dap_log_messages
5522 .as_ref()
5523 },
5524 write: |settings_content, value| {
5525 settings_content
5526 .debugger
5527 .get_or_insert_default()
5528 .format_dap_log_messages = value;
5529 },
5530 }),
5531 metadata: None,
5532 files: USER,
5533 }),
5534 ]
5535 }
5536
5537 SettingsPage {
5538 title: "Debugger",
5539 items: concat_sections![general_section()],
5540 }
5541}
5542
5543fn terminal_page() -> SettingsPage {
5544 fn environment_section() -> [SettingsPageItem; 5] {
5545 [
5546 SettingsPageItem::SectionHeader("Environment"),
5547 SettingsPageItem::DynamicItem(DynamicItem {
5548 discriminant: SettingItem {
5549 files: USER | PROJECT,
5550 title: "Shell",
5551 description: "What shell to use when opening a terminal.",
5552 field: Box::new(SettingField {
5553 json_path: Some("terminal.shell$"),
5554 pick: |settings_content| {
5555 Some(&dynamic_variants::<settings::Shell>()[
5556 settings_content
5557 .terminal
5558 .as_ref()?
5559 .project
5560 .shell
5561 .as_ref()?
5562 .discriminant() as usize
5563 ])
5564 },
5565 write: |settings_content, value| {
5566 let Some(value) = value else {
5567 if let Some(terminal) = settings_content.terminal.as_mut() {
5568 terminal.project.shell = None;
5569 }
5570 return;
5571 };
5572 let settings_value = settings_content
5573 .terminal
5574 .get_or_insert_default()
5575 .project
5576 .shell
5577 .get_or_insert_with(|| settings::Shell::default());
5578 let default_shell = if cfg!(target_os = "windows") {
5579 "powershell.exe"
5580 } else {
5581 "sh"
5582 };
5583 *settings_value = match value {
5584 settings::ShellDiscriminants::System => settings::Shell::System,
5585 settings::ShellDiscriminants::Program => {
5586 let program = match settings_value {
5587 settings::Shell::Program(program) => program.clone(),
5588 settings::Shell::WithArguments { program, .. } => program.clone(),
5589 _ => String::from(default_shell),
5590 };
5591 settings::Shell::Program(program)
5592 }
5593 settings::ShellDiscriminants::WithArguments => {
5594 let (program, args, title_override) = match settings_value {
5595 settings::Shell::Program(program) => (program.clone(), vec![], None),
5596 settings::Shell::WithArguments {
5597 program,
5598 args,
5599 title_override,
5600 } => (program.clone(), args.clone(), title_override.clone()),
5601 _ => (String::from(default_shell), vec![], None),
5602 };
5603 settings::Shell::WithArguments {
5604 program,
5605 args,
5606 title_override,
5607 }
5608 }
5609 };
5610 },
5611 }),
5612 metadata: None,
5613 },
5614 pick_discriminant: |settings_content| {
5615 Some(
5616 settings_content
5617 .terminal
5618 .as_ref()?
5619 .project
5620 .shell
5621 .as_ref()?
5622 .discriminant() as usize,
5623 )
5624 },
5625 fields: dynamic_variants::<settings::Shell>()
5626 .into_iter()
5627 .map(|variant| match variant {
5628 settings::ShellDiscriminants::System => vec![],
5629 settings::ShellDiscriminants::Program => vec![SettingItem {
5630 files: USER | PROJECT,
5631 title: "Program",
5632 description: "The shell program to use.",
5633 field: Box::new(SettingField {
5634 json_path: Some("terminal.shell"),
5635 pick: |settings_content| match settings_content.terminal.as_ref()?.project.shell.as_ref()
5636 {
5637 Some(settings::Shell::Program(program)) => Some(program),
5638 _ => None,
5639 },
5640 write: |settings_content, value| {
5641 let Some(value) = value else {
5642 return;
5643 };
5644 match settings_content
5645 .terminal
5646 .get_or_insert_default()
5647 .project
5648 .shell
5649 .as_mut()
5650 {
5651 Some(settings::Shell::Program(program)) => *program = value,
5652 _ => return,
5653 }
5654 },
5655 }),
5656 metadata: None,
5657 }],
5658 settings::ShellDiscriminants::WithArguments => vec![
5659 SettingItem {
5660 files: USER | PROJECT,
5661 title: "Program",
5662 description: "The shell program to run.",
5663 field: Box::new(SettingField {
5664 json_path: Some("terminal.shell.program"),
5665 pick: |settings_content| {
5666 match settings_content.terminal.as_ref()?.project.shell.as_ref() {
5667 Some(settings::Shell::WithArguments { program, .. }) => Some(program),
5668 _ => None,
5669 }
5670 },
5671 write: |settings_content, value| {
5672 let Some(value) = value else {
5673 return;
5674 };
5675 match settings_content
5676 .terminal
5677 .get_or_insert_default()
5678 .project
5679 .shell
5680 .as_mut()
5681 {
5682 Some(settings::Shell::WithArguments { program, .. }) => {
5683 *program = value
5684 }
5685 _ => return,
5686 }
5687 },
5688 }),
5689 metadata: None,
5690 },
5691 SettingItem {
5692 files: USER | PROJECT,
5693 title: "Arguments",
5694 description: "The arguments to pass to the shell program.",
5695 field: Box::new(
5696 SettingField {
5697 json_path: Some("terminal.shell.args"),
5698 pick: |settings_content| {
5699 match settings_content.terminal.as_ref()?.project.shell.as_ref() {
5700 Some(settings::Shell::WithArguments { args, .. }) => Some(args),
5701 _ => None,
5702 }
5703 },
5704 write: |settings_content, value| {
5705 let Some(value) = value else {
5706 return;
5707 };
5708 match settings_content
5709 .terminal
5710 .get_or_insert_default()
5711 .project
5712 .shell
5713 .as_mut()
5714 {
5715 Some(settings::Shell::WithArguments { args, .. }) => *args = value,
5716 _ => return,
5717 }
5718 },
5719 }
5720 .unimplemented(),
5721 ),
5722 metadata: None,
5723 },
5724 SettingItem {
5725 files: USER | PROJECT,
5726 title: "Title Override",
5727 description: "An optional string to override the title of the terminal tab.",
5728 field: Box::new(SettingField {
5729 json_path: Some("terminal.shell.title_override"),
5730 pick: |settings_content| {
5731 match settings_content.terminal.as_ref()?.project.shell.as_ref() {
5732 Some(settings::Shell::WithArguments { title_override, .. }) => {
5733 title_override.as_ref().or(DEFAULT_EMPTY_STRING)
5734 }
5735 _ => None,
5736 }
5737 },
5738 write: |settings_content, value| {
5739 match settings_content
5740 .terminal
5741 .get_or_insert_default()
5742 .project
5743 .shell
5744 .as_mut()
5745 {
5746 Some(settings::Shell::WithArguments { title_override, .. }) => {
5747 *title_override = value.filter(|s| !s.is_empty())
5748 }
5749 _ => return,
5750 }
5751 },
5752 }),
5753 metadata: None,
5754 },
5755 ],
5756 })
5757 .collect(),
5758 }),
5759 SettingsPageItem::DynamicItem(DynamicItem {
5760 discriminant: SettingItem {
5761 files: USER | PROJECT,
5762 title: "Working Directory",
5763 description: "What working directory to use when launching the terminal.",
5764 field: Box::new(SettingField {
5765 json_path: Some("terminal.working_directory$"),
5766 pick: |settings_content| {
5767 Some(&dynamic_variants::<settings::WorkingDirectory>()[
5768 settings_content
5769 .terminal
5770 .as_ref()?
5771 .project
5772 .working_directory
5773 .as_ref()?
5774 .discriminant() as usize
5775 ])
5776 },
5777 write: |settings_content, value| {
5778 let Some(value) = value else {
5779 if let Some(terminal) = settings_content.terminal.as_mut() {
5780 terminal.project.working_directory = None;
5781 }
5782 return;
5783 };
5784 let settings_value = settings_content
5785 .terminal
5786 .get_or_insert_default()
5787 .project
5788 .working_directory
5789 .get_or_insert_with(|| settings::WorkingDirectory::CurrentProjectDirectory);
5790 *settings_value = match value {
5791 settings::WorkingDirectoryDiscriminants::CurrentFileDirectory => {
5792 settings::WorkingDirectory::CurrentFileDirectory
5793 },
5794 settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => {
5795 settings::WorkingDirectory::CurrentProjectDirectory
5796 }
5797 settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => {
5798 settings::WorkingDirectory::FirstProjectDirectory
5799 }
5800 settings::WorkingDirectoryDiscriminants::AlwaysHome => {
5801 settings::WorkingDirectory::AlwaysHome
5802 }
5803 settings::WorkingDirectoryDiscriminants::Always => {
5804 let directory = match settings_value {
5805 settings::WorkingDirectory::Always { .. } => return,
5806 _ => String::new(),
5807 };
5808 settings::WorkingDirectory::Always { directory }
5809 }
5810 };
5811 },
5812 }),
5813 metadata: None,
5814 },
5815 pick_discriminant: |settings_content| {
5816 Some(
5817 settings_content
5818 .terminal
5819 .as_ref()?
5820 .project
5821 .working_directory
5822 .as_ref()?
5823 .discriminant() as usize,
5824 )
5825 },
5826 fields: dynamic_variants::<settings::WorkingDirectory>()
5827 .into_iter()
5828 .map(|variant| match variant {
5829 settings::WorkingDirectoryDiscriminants::CurrentFileDirectory => vec![],
5830 settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => vec![],
5831 settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => vec![],
5832 settings::WorkingDirectoryDiscriminants::AlwaysHome => vec![],
5833 settings::WorkingDirectoryDiscriminants::Always => vec![SettingItem {
5834 files: USER | PROJECT,
5835 title: "Directory",
5836 description: "The directory path to use (will be shell expanded).",
5837 field: Box::new(SettingField {
5838 json_path: Some("terminal.working_directory.always"),
5839 pick: |settings_content| {
5840 match settings_content.terminal.as_ref()?.project.working_directory.as_ref() {
5841 Some(settings::WorkingDirectory::Always { directory }) => Some(directory),
5842 _ => None,
5843 }
5844 },
5845 write: |settings_content, value| {
5846 let value = value.unwrap_or_default();
5847 match settings_content
5848 .terminal
5849 .get_or_insert_default()
5850 .project
5851 .working_directory
5852 .as_mut()
5853 {
5854 Some(settings::WorkingDirectory::Always { directory }) => *directory = value,
5855 _ => return,
5856 }
5857 },
5858 }),
5859 metadata: None,
5860 }],
5861 })
5862 .collect(),
5863 }),
5864 SettingsPageItem::SettingItem(SettingItem {
5865 title: "Environment Variables",
5866 description: "Key-value pairs to add to the terminal's environment.",
5867 field: Box::new(
5868 SettingField {
5869 json_path: Some("terminal.env"),
5870 pick: |settings_content| settings_content.terminal.as_ref()?.project.env.as_ref(),
5871 write: |settings_content, value| {
5872 settings_content.terminal.get_or_insert_default().project.env = value;
5873 },
5874 }
5875 .unimplemented(),
5876 ),
5877 metadata: None,
5878 files: USER | PROJECT,
5879 }),
5880 SettingsPageItem::SettingItem(SettingItem {
5881 title: "Detect Virtual Environment",
5882 description: "Activates the Python virtual environment, if one is found, in the terminal's working directory.",
5883 field: Box::new(
5884 SettingField {
5885 json_path: Some("terminal.detect_venv"),
5886 pick: |settings_content| settings_content.terminal.as_ref()?.project.detect_venv.as_ref(),
5887 write: |settings_content, value| {
5888 settings_content
5889 .terminal
5890 .get_or_insert_default()
5891 .project
5892 .detect_venv = value;
5893 },
5894 }
5895 .unimplemented(),
5896 ),
5897 metadata: None,
5898 files: USER | PROJECT,
5899 }),
5900 ]
5901 }
5902
5903 fn font_section() -> [SettingsPageItem; 6] {
5904 [
5905 SettingsPageItem::SectionHeader("Font"),
5906 SettingsPageItem::SettingItem(SettingItem {
5907 title: "Font Size",
5908 description: "Font size for terminal text. If not set, defaults to buffer font size.",
5909 field: Box::new(SettingField {
5910 json_path: Some("terminal.font_size"),
5911 pick: |settings_content| {
5912 settings_content
5913 .terminal
5914 .as_ref()
5915 .and_then(|terminal| terminal.font_size.as_ref())
5916 .or(settings_content.theme.buffer_font_size.as_ref())
5917 },
5918 write: |settings_content, value| {
5919 settings_content.terminal.get_or_insert_default().font_size = value;
5920 },
5921 }),
5922 metadata: None,
5923 files: USER,
5924 }),
5925 SettingsPageItem::SettingItem(SettingItem {
5926 title: "Font Family",
5927 description: "Font family for terminal text. If not set, defaults to buffer font family.",
5928 field: Box::new(SettingField {
5929 json_path: Some("terminal.font_family"),
5930 pick: |settings_content| {
5931 settings_content
5932 .terminal
5933 .as_ref()
5934 .and_then(|terminal| terminal.font_family.as_ref())
5935 .or(settings_content.theme.buffer_font_family.as_ref())
5936 },
5937 write: |settings_content, value| {
5938 settings_content
5939 .terminal
5940 .get_or_insert_default()
5941 .font_family = value;
5942 },
5943 }),
5944 metadata: None,
5945 files: USER,
5946 }),
5947 SettingsPageItem::SettingItem(SettingItem {
5948 title: "Font Fallbacks",
5949 description: "Font fallbacks for terminal text. If not set, defaults to buffer font fallbacks.",
5950 field: Box::new(
5951 SettingField {
5952 json_path: Some("terminal.font_fallbacks"),
5953 pick: |settings_content| {
5954 settings_content
5955 .terminal
5956 .as_ref()
5957 .and_then(|terminal| terminal.font_fallbacks.as_ref())
5958 .or(settings_content.theme.buffer_font_fallbacks.as_ref())
5959 },
5960 write: |settings_content, value| {
5961 settings_content
5962 .terminal
5963 .get_or_insert_default()
5964 .font_fallbacks = value;
5965 },
5966 }
5967 .unimplemented(),
5968 ),
5969 metadata: None,
5970 files: USER,
5971 }),
5972 SettingsPageItem::SettingItem(SettingItem {
5973 title: "Font Weight",
5974 description: "Font weight for terminal text in CSS weight units (100-900).",
5975 field: Box::new(SettingField {
5976 json_path: Some("terminal.font_weight"),
5977 pick: |settings_content| {
5978 settings_content.terminal.as_ref()?.font_weight.as_ref()
5979 },
5980 write: |settings_content, value| {
5981 settings_content
5982 .terminal
5983 .get_or_insert_default()
5984 .font_weight = value;
5985 },
5986 }),
5987 metadata: None,
5988 files: USER,
5989 }),
5990 SettingsPageItem::SettingItem(SettingItem {
5991 title: "Font Features",
5992 description: "Font features for terminal text.",
5993 field: Box::new(
5994 SettingField {
5995 json_path: Some("terminal.font_features"),
5996 pick: |settings_content| {
5997 settings_content
5998 .terminal
5999 .as_ref()
6000 .and_then(|terminal| terminal.font_features.as_ref())
6001 .or(settings_content.theme.buffer_font_features.as_ref())
6002 },
6003 write: |settings_content, value| {
6004 settings_content
6005 .terminal
6006 .get_or_insert_default()
6007 .font_features = value;
6008 },
6009 }
6010 .unimplemented(),
6011 ),
6012 metadata: None,
6013 files: USER,
6014 }),
6015 ]
6016 }
6017
6018 fn display_settings_section() -> [SettingsPageItem; 6] {
6019 [
6020 SettingsPageItem::SectionHeader("Display Settings"),
6021 SettingsPageItem::SettingItem(SettingItem {
6022 title: "Line Height",
6023 description: "Line height for terminal text.",
6024 field: Box::new(
6025 SettingField {
6026 json_path: Some("terminal.line_height"),
6027 pick: |settings_content| {
6028 settings_content.terminal.as_ref()?.line_height.as_ref()
6029 },
6030 write: |settings_content, value| {
6031 settings_content
6032 .terminal
6033 .get_or_insert_default()
6034 .line_height = value;
6035 },
6036 }
6037 .unimplemented(),
6038 ),
6039 metadata: None,
6040 files: USER,
6041 }),
6042 SettingsPageItem::SettingItem(SettingItem {
6043 title: "Cursor Shape",
6044 description: "Default cursor shape for the terminal (bar, block, underline, or hollow).",
6045 field: Box::new(SettingField {
6046 json_path: Some("terminal.cursor_shape"),
6047 pick: |settings_content| {
6048 settings_content.terminal.as_ref()?.cursor_shape.as_ref()
6049 },
6050 write: |settings_content, value| {
6051 settings_content
6052 .terminal
6053 .get_or_insert_default()
6054 .cursor_shape = value;
6055 },
6056 }),
6057 metadata: None,
6058 files: USER,
6059 }),
6060 SettingsPageItem::SettingItem(SettingItem {
6061 title: "Cursor Blinking",
6062 description: "Sets the cursor blinking behavior in the terminal.",
6063 field: Box::new(SettingField {
6064 json_path: Some("terminal.blinking"),
6065 pick: |settings_content| settings_content.terminal.as_ref()?.blinking.as_ref(),
6066 write: |settings_content, value| {
6067 settings_content.terminal.get_or_insert_default().blinking = value;
6068 },
6069 }),
6070 metadata: None,
6071 files: USER,
6072 }),
6073 SettingsPageItem::SettingItem(SettingItem {
6074 title: "Alternate Scroll",
6075 description: "Whether alternate scroll mode is active by default (converts mouse scroll to arrow keys in apps like Vim).",
6076 field: Box::new(SettingField {
6077 json_path: Some("terminal.alternate_scroll"),
6078 pick: |settings_content| {
6079 settings_content
6080 .terminal
6081 .as_ref()?
6082 .alternate_scroll
6083 .as_ref()
6084 },
6085 write: |settings_content, value| {
6086 settings_content
6087 .terminal
6088 .get_or_insert_default()
6089 .alternate_scroll = value;
6090 },
6091 }),
6092 metadata: None,
6093 files: USER,
6094 }),
6095 SettingsPageItem::SettingItem(SettingItem {
6096 title: "Minimum Contrast",
6097 description: "The minimum APCA perceptual contrast between foreground and background colors (0-106).",
6098 field: Box::new(SettingField {
6099 json_path: Some("terminal.minimum_contrast"),
6100 pick: |settings_content| {
6101 settings_content
6102 .terminal
6103 .as_ref()?
6104 .minimum_contrast
6105 .as_ref()
6106 },
6107 write: |settings_content, value| {
6108 settings_content
6109 .terminal
6110 .get_or_insert_default()
6111 .minimum_contrast = value;
6112 },
6113 }),
6114 metadata: None,
6115 files: USER,
6116 }),
6117 ]
6118 }
6119
6120 fn behavior_settings_section() -> [SettingsPageItem; 4] {
6121 [
6122 SettingsPageItem::SectionHeader("Behavior Settings"),
6123 SettingsPageItem::SettingItem(SettingItem {
6124 title: "Option As Meta",
6125 description: "Whether the option key behaves as the meta key.",
6126 field: Box::new(SettingField {
6127 json_path: Some("terminal.option_as_meta"),
6128 pick: |settings_content| {
6129 settings_content.terminal.as_ref()?.option_as_meta.as_ref()
6130 },
6131 write: |settings_content, value| {
6132 settings_content
6133 .terminal
6134 .get_or_insert_default()
6135 .option_as_meta = value;
6136 },
6137 }),
6138 metadata: None,
6139 files: USER,
6140 }),
6141 SettingsPageItem::SettingItem(SettingItem {
6142 title: "Copy On Select",
6143 description: "Whether selecting text in the terminal automatically copies to the system clipboard.",
6144 field: Box::new(SettingField {
6145 json_path: Some("terminal.copy_on_select"),
6146 pick: |settings_content| {
6147 settings_content.terminal.as_ref()?.copy_on_select.as_ref()
6148 },
6149 write: |settings_content, value| {
6150 settings_content
6151 .terminal
6152 .get_or_insert_default()
6153 .copy_on_select = value;
6154 },
6155 }),
6156 metadata: None,
6157 files: USER,
6158 }),
6159 SettingsPageItem::SettingItem(SettingItem {
6160 title: "Keep Selection On Copy",
6161 description: "Whether to keep the text selection after copying it to the clipboard.",
6162 field: Box::new(SettingField {
6163 json_path: Some("terminal.keep_selection_on_copy"),
6164 pick: |settings_content| {
6165 settings_content
6166 .terminal
6167 .as_ref()?
6168 .keep_selection_on_copy
6169 .as_ref()
6170 },
6171 write: |settings_content, value| {
6172 settings_content
6173 .terminal
6174 .get_or_insert_default()
6175 .keep_selection_on_copy = value;
6176 },
6177 }),
6178 metadata: None,
6179 files: USER,
6180 }),
6181 ]
6182 }
6183
6184 fn layout_settings_section() -> [SettingsPageItem; 3] {
6185 [
6186 SettingsPageItem::SectionHeader("Layout Settings"),
6187 SettingsPageItem::SettingItem(SettingItem {
6188 title: "Default Width",
6189 description: "Default width when the terminal is docked to the left or right (in pixels).",
6190 field: Box::new(SettingField {
6191 json_path: Some("terminal.default_width"),
6192 pick: |settings_content| {
6193 settings_content.terminal.as_ref()?.default_width.as_ref()
6194 },
6195 write: |settings_content, value| {
6196 settings_content
6197 .terminal
6198 .get_or_insert_default()
6199 .default_width = value;
6200 },
6201 }),
6202 metadata: None,
6203 files: USER,
6204 }),
6205 SettingsPageItem::SettingItem(SettingItem {
6206 title: "Default Height",
6207 description: "Default height when the terminal is docked to the bottom (in pixels).",
6208 field: Box::new(SettingField {
6209 json_path: Some("terminal.default_height"),
6210 pick: |settings_content| {
6211 settings_content.terminal.as_ref()?.default_height.as_ref()
6212 },
6213 write: |settings_content, value| {
6214 settings_content
6215 .terminal
6216 .get_or_insert_default()
6217 .default_height = value;
6218 },
6219 }),
6220 metadata: None,
6221 files: USER,
6222 }),
6223 ]
6224 }
6225
6226 fn advanced_settings_section() -> [SettingsPageItem; 3] {
6227 [
6228 SettingsPageItem::SectionHeader("Advanced Settings"),
6229 SettingsPageItem::SettingItem(SettingItem {
6230 title: "Max Scroll History Lines",
6231 description: "Maximum number of lines to keep in scrollback history (max: 100,000; 0 disables scrolling).",
6232 field: Box::new(SettingField {
6233 json_path: Some("terminal.max_scroll_history_lines"),
6234 pick: |settings_content| {
6235 settings_content
6236 .terminal
6237 .as_ref()?
6238 .max_scroll_history_lines
6239 .as_ref()
6240 },
6241 write: |settings_content, value| {
6242 settings_content
6243 .terminal
6244 .get_or_insert_default()
6245 .max_scroll_history_lines = value;
6246 },
6247 }),
6248 metadata: None,
6249 files: USER,
6250 }),
6251 SettingsPageItem::SettingItem(SettingItem {
6252 title: "Scroll Multiplier",
6253 description: "The multiplier for scrolling in the terminal with the mouse wheel",
6254 field: Box::new(SettingField {
6255 json_path: Some("terminal.scroll_multiplier"),
6256 pick: |settings_content| {
6257 settings_content
6258 .terminal
6259 .as_ref()?
6260 .scroll_multiplier
6261 .as_ref()
6262 },
6263 write: |settings_content, value| {
6264 settings_content
6265 .terminal
6266 .get_or_insert_default()
6267 .scroll_multiplier = value;
6268 },
6269 }),
6270 metadata: None,
6271 files: USER,
6272 }),
6273 ]
6274 }
6275
6276 fn toolbar_section() -> [SettingsPageItem; 2] {
6277 [
6278 SettingsPageItem::SectionHeader("Toolbar"),
6279 SettingsPageItem::SettingItem(SettingItem {
6280 title: "Breadcrumbs",
6281 description: "Display the terminal title in breadcrumbs inside the terminal pane.",
6282 field: Box::new(SettingField {
6283 json_path: Some("terminal.toolbar.breadcrumbs"),
6284 pick: |settings_content| {
6285 settings_content
6286 .terminal
6287 .as_ref()?
6288 .toolbar
6289 .as_ref()?
6290 .breadcrumbs
6291 .as_ref()
6292 },
6293 write: |settings_content, value| {
6294 settings_content
6295 .terminal
6296 .get_or_insert_default()
6297 .toolbar
6298 .get_or_insert_default()
6299 .breadcrumbs = value;
6300 },
6301 }),
6302 metadata: None,
6303 files: USER,
6304 }),
6305 ]
6306 }
6307
6308 fn scrollbar_section() -> [SettingsPageItem; 2] {
6309 [
6310 SettingsPageItem::SectionHeader("Scrollbar"),
6311 SettingsPageItem::SettingItem(SettingItem {
6312 title: "Show Scrollbar",
6313 description: "When to show the scrollbar in the terminal.",
6314 field: Box::new(SettingField {
6315 json_path: Some("terminal.scrollbar.show"),
6316 pick: |settings_content| {
6317 show_scrollbar_or_editor(settings_content, |settings_content| {
6318 settings_content
6319 .terminal
6320 .as_ref()?
6321 .scrollbar
6322 .as_ref()?
6323 .show
6324 .as_ref()
6325 })
6326 },
6327 write: |settings_content, value| {
6328 settings_content
6329 .terminal
6330 .get_or_insert_default()
6331 .scrollbar
6332 .get_or_insert_default()
6333 .show = value;
6334 },
6335 }),
6336 metadata: None,
6337 files: USER,
6338 }),
6339 ]
6340 }
6341
6342 SettingsPage {
6343 title: "Terminal",
6344 items: concat_sections![
6345 environment_section(),
6346 font_section(),
6347 display_settings_section(),
6348 behavior_settings_section(),
6349 layout_settings_section(),
6350 advanced_settings_section(),
6351 toolbar_section(),
6352 scrollbar_section(),
6353 ],
6354 }
6355}
6356
6357fn version_control_page() -> SettingsPage {
6358 fn git_integration_section() -> [SettingsPageItem; 2] {
6359 [
6360 SettingsPageItem::SectionHeader("Git Integration"),
6361 SettingsPageItem::DynamicItem(DynamicItem {
6362 discriminant: SettingItem {
6363 files: USER,
6364 title: "Disable Git Integration",
6365 description: "Disable all Git integration features in Zed.",
6366 field: Box::new(SettingField::<bool> {
6367 json_path: Some("git.disable_git"),
6368 pick: |settings_content| {
6369 settings_content
6370 .git
6371 .as_ref()?
6372 .enabled
6373 .as_ref()?
6374 .disable_git
6375 .as_ref()
6376 },
6377 write: |settings_content, value| {
6378 settings_content
6379 .git
6380 .get_or_insert_default()
6381 .enabled
6382 .get_or_insert_default()
6383 .disable_git = value;
6384 },
6385 }),
6386 metadata: None,
6387 },
6388 pick_discriminant: |settings_content| {
6389 let disabled = settings_content
6390 .git
6391 .as_ref()?
6392 .enabled
6393 .as_ref()?
6394 .disable_git
6395 .unwrap_or(false);
6396 Some(if disabled { 0 } else { 1 })
6397 },
6398 fields: vec![
6399 vec![],
6400 vec![
6401 SettingItem {
6402 files: USER,
6403 title: "Enable Git Status",
6404 description: "Show Git status information in the editor.",
6405 field: Box::new(SettingField::<bool> {
6406 json_path: Some("git.enable_status"),
6407 pick: |settings_content| {
6408 settings_content
6409 .git
6410 .as_ref()?
6411 .enabled
6412 .as_ref()?
6413 .enable_status
6414 .as_ref()
6415 },
6416 write: |settings_content, value| {
6417 settings_content
6418 .git
6419 .get_or_insert_default()
6420 .enabled
6421 .get_or_insert_default()
6422 .enable_status = value;
6423 },
6424 }),
6425 metadata: None,
6426 },
6427 SettingItem {
6428 files: USER,
6429 title: "Enable Git Diff",
6430 description: "Show Git diff information in the editor.",
6431 field: Box::new(SettingField::<bool> {
6432 json_path: Some("git.enable_diff"),
6433 pick: |settings_content| {
6434 settings_content
6435 .git
6436 .as_ref()?
6437 .enabled
6438 .as_ref()?
6439 .enable_diff
6440 .as_ref()
6441 },
6442 write: |settings_content, value| {
6443 settings_content
6444 .git
6445 .get_or_insert_default()
6446 .enabled
6447 .get_or_insert_default()
6448 .enable_diff = value;
6449 },
6450 }),
6451 metadata: None,
6452 },
6453 ],
6454 ],
6455 }),
6456 ]
6457 }
6458
6459 fn git_gutter_section() -> [SettingsPageItem; 3] {
6460 [
6461 SettingsPageItem::SectionHeader("Git Gutter"),
6462 SettingsPageItem::SettingItem(SettingItem {
6463 title: "Visibility",
6464 description: "Control whether Git status is shown in the editor's gutter.",
6465 field: Box::new(SettingField {
6466 json_path: Some("git.git_gutter"),
6467 pick: |settings_content| settings_content.git.as_ref()?.git_gutter.as_ref(),
6468 write: |settings_content, value| {
6469 settings_content.git.get_or_insert_default().git_gutter = value;
6470 },
6471 }),
6472 metadata: None,
6473 files: USER,
6474 }),
6475 // todo(settings_ui): Figure out the right default for this value in default.json
6476 SettingsPageItem::SettingItem(SettingItem {
6477 title: "Debounce",
6478 description: "Debounce threshold in milliseconds after which changes are reflected in the Git gutter.",
6479 field: Box::new(SettingField {
6480 json_path: Some("git.gutter_debounce"),
6481 pick: |settings_content| {
6482 settings_content.git.as_ref()?.gutter_debounce.as_ref()
6483 },
6484 write: |settings_content, value| {
6485 settings_content.git.get_or_insert_default().gutter_debounce = value;
6486 },
6487 }),
6488 metadata: None,
6489 files: USER,
6490 }),
6491 ]
6492 }
6493
6494 fn inline_git_blame_section() -> [SettingsPageItem; 6] {
6495 [
6496 SettingsPageItem::SectionHeader("Inline Git Blame"),
6497 SettingsPageItem::SettingItem(SettingItem {
6498 title: "Enabled",
6499 description: "Whether or not to show Git blame data inline in the currently focused line.",
6500 field: Box::new(SettingField {
6501 json_path: Some("git.inline_blame.enabled"),
6502 pick: |settings_content| {
6503 settings_content
6504 .git
6505 .as_ref()?
6506 .inline_blame
6507 .as_ref()?
6508 .enabled
6509 .as_ref()
6510 },
6511 write: |settings_content, value| {
6512 settings_content
6513 .git
6514 .get_or_insert_default()
6515 .inline_blame
6516 .get_or_insert_default()
6517 .enabled = value;
6518 },
6519 }),
6520 metadata: None,
6521 files: USER,
6522 }),
6523 SettingsPageItem::SettingItem(SettingItem {
6524 title: "Delay",
6525 description: "The delay after which the inline blame information is shown.",
6526 field: Box::new(SettingField {
6527 json_path: Some("git.inline_blame.delay_ms"),
6528 pick: |settings_content| {
6529 settings_content
6530 .git
6531 .as_ref()?
6532 .inline_blame
6533 .as_ref()?
6534 .delay_ms
6535 .as_ref()
6536 },
6537 write: |settings_content, value| {
6538 settings_content
6539 .git
6540 .get_or_insert_default()
6541 .inline_blame
6542 .get_or_insert_default()
6543 .delay_ms = value;
6544 },
6545 }),
6546 metadata: None,
6547 files: USER,
6548 }),
6549 SettingsPageItem::SettingItem(SettingItem {
6550 title: "Padding",
6551 description: "Padding between the end of the source line and the start of the inline blame in columns.",
6552 field: Box::new(SettingField {
6553 json_path: Some("git.inline_blame.padding"),
6554 pick: |settings_content| {
6555 settings_content
6556 .git
6557 .as_ref()?
6558 .inline_blame
6559 .as_ref()?
6560 .padding
6561 .as_ref()
6562 },
6563 write: |settings_content, value| {
6564 settings_content
6565 .git
6566 .get_or_insert_default()
6567 .inline_blame
6568 .get_or_insert_default()
6569 .padding = value;
6570 },
6571 }),
6572 metadata: None,
6573 files: USER,
6574 }),
6575 SettingsPageItem::SettingItem(SettingItem {
6576 title: "Minimum Column",
6577 description: "The minimum column number at which to show the inline blame information.",
6578 field: Box::new(SettingField {
6579 json_path: Some("git.inline_blame.min_column"),
6580 pick: |settings_content| {
6581 settings_content
6582 .git
6583 .as_ref()?
6584 .inline_blame
6585 .as_ref()?
6586 .min_column
6587 .as_ref()
6588 },
6589 write: |settings_content, value| {
6590 settings_content
6591 .git
6592 .get_or_insert_default()
6593 .inline_blame
6594 .get_or_insert_default()
6595 .min_column = value;
6596 },
6597 }),
6598 metadata: None,
6599 files: USER,
6600 }),
6601 SettingsPageItem::SettingItem(SettingItem {
6602 title: "Show Commit Summary",
6603 description: "Show commit summary as part of the inline blame.",
6604 field: Box::new(SettingField {
6605 json_path: Some("git.inline_blame.show_commit_summary"),
6606 pick: |settings_content| {
6607 settings_content
6608 .git
6609 .as_ref()?
6610 .inline_blame
6611 .as_ref()?
6612 .show_commit_summary
6613 .as_ref()
6614 },
6615 write: |settings_content, value| {
6616 settings_content
6617 .git
6618 .get_or_insert_default()
6619 .inline_blame
6620 .get_or_insert_default()
6621 .show_commit_summary = value;
6622 },
6623 }),
6624 metadata: None,
6625 files: USER,
6626 }),
6627 ]
6628 }
6629
6630 fn git_blame_view_section() -> [SettingsPageItem; 2] {
6631 [
6632 SettingsPageItem::SectionHeader("Git Blame View"),
6633 SettingsPageItem::SettingItem(SettingItem {
6634 title: "Show Avatar",
6635 description: "Show the avatar of the author of the commit.",
6636 field: Box::new(SettingField {
6637 json_path: Some("git.blame.show_avatar"),
6638 pick: |settings_content| {
6639 settings_content
6640 .git
6641 .as_ref()?
6642 .blame
6643 .as_ref()?
6644 .show_avatar
6645 .as_ref()
6646 },
6647 write: |settings_content, value| {
6648 settings_content
6649 .git
6650 .get_or_insert_default()
6651 .blame
6652 .get_or_insert_default()
6653 .show_avatar = value;
6654 },
6655 }),
6656 metadata: None,
6657 files: USER,
6658 }),
6659 ]
6660 }
6661
6662 fn branch_picker_section() -> [SettingsPageItem; 2] {
6663 [
6664 SettingsPageItem::SectionHeader("Branch Picker"),
6665 SettingsPageItem::SettingItem(SettingItem {
6666 title: "Show Author Name",
6667 description: "Show author name as part of the commit information in branch picker.",
6668 field: Box::new(SettingField {
6669 json_path: Some("git.branch_picker.show_author_name"),
6670 pick: |settings_content| {
6671 settings_content
6672 .git
6673 .as_ref()?
6674 .branch_picker
6675 .as_ref()?
6676 .show_author_name
6677 .as_ref()
6678 },
6679 write: |settings_content, value| {
6680 settings_content
6681 .git
6682 .get_or_insert_default()
6683 .branch_picker
6684 .get_or_insert_default()
6685 .show_author_name = value;
6686 },
6687 }),
6688 metadata: None,
6689 files: USER,
6690 }),
6691 ]
6692 }
6693
6694 fn git_hunks_section() -> [SettingsPageItem; 3] {
6695 [
6696 SettingsPageItem::SectionHeader("Git Hunks"),
6697 SettingsPageItem::SettingItem(SettingItem {
6698 title: "Hunk Style",
6699 description: "How Git hunks are displayed visually in the editor.",
6700 field: Box::new(SettingField {
6701 json_path: Some("git.hunk_style"),
6702 pick: |settings_content| settings_content.git.as_ref()?.hunk_style.as_ref(),
6703 write: |settings_content, value| {
6704 settings_content.git.get_or_insert_default().hunk_style = value;
6705 },
6706 }),
6707 metadata: None,
6708 files: USER,
6709 }),
6710 SettingsPageItem::SettingItem(SettingItem {
6711 title: "Path Style",
6712 description: "Should the name or path be displayed first in the git view.",
6713 field: Box::new(SettingField {
6714 json_path: Some("git.path_style"),
6715 pick: |settings_content| settings_content.git.as_ref()?.path_style.as_ref(),
6716 write: |settings_content, value| {
6717 settings_content.git.get_or_insert_default().path_style = value;
6718 },
6719 }),
6720 metadata: None,
6721 files: USER,
6722 }),
6723 ]
6724 }
6725
6726 SettingsPage {
6727 title: "Version Control",
6728 items: concat_sections![
6729 git_integration_section(),
6730 git_gutter_section(),
6731 inline_git_blame_section(),
6732 git_blame_view_section(),
6733 branch_picker_section(),
6734 git_hunks_section(),
6735 ],
6736 }
6737}
6738
6739fn collaboration_page() -> SettingsPage {
6740 fn calls_section() -> [SettingsPageItem; 3] {
6741 [
6742 SettingsPageItem::SectionHeader("Calls"),
6743 SettingsPageItem::SettingItem(SettingItem {
6744 title: "Mute On Join",
6745 description: "Whether the microphone should be muted when joining a channel or a call.",
6746 field: Box::new(SettingField {
6747 json_path: Some("calls.mute_on_join"),
6748 pick: |settings_content| settings_content.calls.as_ref()?.mute_on_join.as_ref(),
6749 write: |settings_content, value| {
6750 settings_content.calls.get_or_insert_default().mute_on_join = value;
6751 },
6752 }),
6753 metadata: None,
6754 files: USER,
6755 }),
6756 SettingsPageItem::SettingItem(SettingItem {
6757 title: "Share On Join",
6758 description: "Whether your current project should be shared when joining an empty channel.",
6759 field: Box::new(SettingField {
6760 json_path: Some("calls.share_on_join"),
6761 pick: |settings_content| {
6762 settings_content.calls.as_ref()?.share_on_join.as_ref()
6763 },
6764 write: |settings_content, value| {
6765 settings_content.calls.get_or_insert_default().share_on_join = value;
6766 },
6767 }),
6768 metadata: None,
6769 files: USER,
6770 }),
6771 ]
6772 }
6773
6774 fn experimental_section() -> [SettingsPageItem; 9] {
6775 [
6776 SettingsPageItem::SectionHeader("Experimental"),
6777 SettingsPageItem::SettingItem(SettingItem {
6778 title: "Rodio Audio",
6779 description: "Opt into the new audio system.",
6780 field: Box::new(SettingField {
6781 json_path: Some("audio.experimental.rodio_audio"),
6782 pick: |settings_content| settings_content.audio.as_ref()?.rodio_audio.as_ref(),
6783 write: |settings_content, value| {
6784 settings_content.audio.get_or_insert_default().rodio_audio = value;
6785 },
6786 }),
6787 metadata: None,
6788 files: USER,
6789 }),
6790 SettingsPageItem::SettingItem(SettingItem {
6791 title: "Auto Microphone Volume",
6792 description: "Automatically adjust microphone volume (requires rodio audio).",
6793 field: Box::new(SettingField {
6794 json_path: Some("audio.experimental.auto_microphone_volume"),
6795 pick: |settings_content| {
6796 settings_content
6797 .audio
6798 .as_ref()?
6799 .auto_microphone_volume
6800 .as_ref()
6801 },
6802 write: |settings_content, value| {
6803 settings_content
6804 .audio
6805 .get_or_insert_default()
6806 .auto_microphone_volume = value;
6807 },
6808 }),
6809 metadata: None,
6810 files: USER,
6811 }),
6812 SettingsPageItem::SettingItem(SettingItem {
6813 title: "Auto Speaker Volume",
6814 description: "Automatically adjust volume of other call members (requires rodio audio).",
6815 field: Box::new(SettingField {
6816 json_path: Some("audio.experimental.auto_speaker_volume"),
6817 pick: |settings_content| {
6818 settings_content
6819 .audio
6820 .as_ref()?
6821 .auto_speaker_volume
6822 .as_ref()
6823 },
6824 write: |settings_content, value| {
6825 settings_content
6826 .audio
6827 .get_or_insert_default()
6828 .auto_speaker_volume = value;
6829 },
6830 }),
6831 metadata: None,
6832 files: USER,
6833 }),
6834 SettingsPageItem::SettingItem(SettingItem {
6835 title: "Denoise",
6836 description: "Remove background noises (requires rodio audio).",
6837 field: Box::new(SettingField {
6838 json_path: Some("audio.experimental.denoise"),
6839 pick: |settings_content| settings_content.audio.as_ref()?.denoise.as_ref(),
6840 write: |settings_content, value| {
6841 settings_content.audio.get_or_insert_default().denoise = value;
6842 },
6843 }),
6844 metadata: None,
6845 files: USER,
6846 }),
6847 SettingsPageItem::SettingItem(SettingItem {
6848 title: "Legacy Audio Compatible",
6849 description: "Use audio parameters compatible with previous versions (requires rodio audio).",
6850 field: Box::new(SettingField {
6851 json_path: Some("audio.experimental.legacy_audio_compatible"),
6852 pick: |settings_content| {
6853 settings_content
6854 .audio
6855 .as_ref()?
6856 .legacy_audio_compatible
6857 .as_ref()
6858 },
6859 write: |settings_content, value| {
6860 settings_content
6861 .audio
6862 .get_or_insert_default()
6863 .legacy_audio_compatible = value;
6864 },
6865 }),
6866 metadata: None,
6867 files: USER,
6868 }),
6869 SettingsPageItem::ActionLink(ActionLink {
6870 title: "Test Audio".into(),
6871 description: Some("Test your microphone and speaker setup".into()),
6872 button_text: "Test Audio".into(),
6873 on_click: Arc::new(|_settings_window, window, cx| {
6874 open_audio_test_window(window, cx);
6875 }),
6876 files: USER,
6877 }),
6878 SettingsPageItem::SettingItem(SettingItem {
6879 title: "Output Audio Device",
6880 description: "Select output audio device",
6881 field: Box::new(SettingField {
6882 json_path: Some("audio.experimental.output_audio_device"),
6883 pick: |settings_content| {
6884 settings_content
6885 .audio
6886 .as_ref()?
6887 .output_audio_device
6888 .as_ref()
6889 .or(DEFAULT_EMPTY_AUDIO_OUTPUT)
6890 },
6891 write: |settings_content, value| {
6892 settings_content
6893 .audio
6894 .get_or_insert_default()
6895 .output_audio_device = value;
6896 },
6897 }),
6898 metadata: None,
6899 files: USER,
6900 }),
6901 SettingsPageItem::SettingItem(SettingItem {
6902 title: "Input Audio Device",
6903 description: "Select input audio device",
6904 field: Box::new(SettingField {
6905 json_path: Some("audio.experimental.input_audio_device"),
6906 pick: |settings_content| {
6907 settings_content
6908 .audio
6909 .as_ref()?
6910 .input_audio_device
6911 .as_ref()
6912 .or(DEFAULT_EMPTY_AUDIO_INPUT)
6913 },
6914 write: |settings_content, value| {
6915 settings_content
6916 .audio
6917 .get_or_insert_default()
6918 .input_audio_device = value;
6919 },
6920 }),
6921 metadata: None,
6922 files: USER,
6923 }),
6924 ]
6925 }
6926
6927 SettingsPage {
6928 title: "Collaboration",
6929 items: concat_sections![calls_section(), experimental_section()],
6930 }
6931}
6932
6933fn ai_page() -> SettingsPage {
6934 fn general_section() -> [SettingsPageItem; 2] {
6935 [
6936 SettingsPageItem::SectionHeader("General"),
6937 SettingsPageItem::SettingItem(SettingItem {
6938 title: "Disable AI",
6939 description: "Whether to disable all AI features in Zed.",
6940 field: Box::new(SettingField {
6941 json_path: Some("disable_ai"),
6942 pick: |settings_content| settings_content.project.disable_ai.as_ref(),
6943 write: |settings_content, value| {
6944 settings_content.project.disable_ai = value;
6945 },
6946 }),
6947 metadata: None,
6948 files: USER | PROJECT,
6949 }),
6950 ]
6951 }
6952
6953 fn agent_configuration_section() -> [SettingsPageItem; 12] {
6954 [
6955 SettingsPageItem::SectionHeader("Agent Configuration"),
6956 SettingsPageItem::SubPageLink(SubPageLink {
6957 title: "Tool Permissions".into(),
6958 r#type: Default::default(),
6959 json_path: Some("agent.tool_permissions"),
6960 description: Some("Set up regex patterns to auto-allow, auto-deny, or always request confirmation, for specific tool inputs.".into()),
6961 in_json: true,
6962 files: USER,
6963 render: render_tool_permissions_setup_page,
6964 }),
6965 SettingsPageItem::SettingItem(SettingItem {
6966 title: "Single File Review",
6967 description: "When enabled, agent edits will also be displayed in single-file buffers for review.",
6968 field: Box::new(SettingField {
6969 json_path: Some("agent.single_file_review"),
6970 pick: |settings_content| {
6971 settings_content.agent.as_ref()?.single_file_review.as_ref()
6972 },
6973 write: |settings_content, value| {
6974 settings_content
6975 .agent
6976 .get_or_insert_default()
6977 .single_file_review = value;
6978 },
6979 }),
6980 metadata: None,
6981 files: USER,
6982 }),
6983 SettingsPageItem::SettingItem(SettingItem {
6984 title: "Enable Feedback",
6985 description: "Show voting thumbs up/down icon buttons for feedback on agent edits.",
6986 field: Box::new(SettingField {
6987 json_path: Some("agent.enable_feedback"),
6988 pick: |settings_content| {
6989 settings_content.agent.as_ref()?.enable_feedback.as_ref()
6990 },
6991 write: |settings_content, value| {
6992 settings_content
6993 .agent
6994 .get_or_insert_default()
6995 .enable_feedback = value;
6996 },
6997 }),
6998 metadata: None,
6999 files: USER,
7000 }),
7001 SettingsPageItem::SettingItem(SettingItem {
7002 title: "Notify When Agent Waiting",
7003 description: "Where to show notifications when the agent has completed its response or needs confirmation before running a tool action.",
7004 field: Box::new(SettingField {
7005 json_path: Some("agent.notify_when_agent_waiting"),
7006 pick: |settings_content| {
7007 settings_content
7008 .agent
7009 .as_ref()?
7010 .notify_when_agent_waiting
7011 .as_ref()
7012 },
7013 write: |settings_content, value| {
7014 settings_content
7015 .agent
7016 .get_or_insert_default()
7017 .notify_when_agent_waiting = value;
7018 },
7019 }),
7020 metadata: None,
7021 files: USER,
7022 }),
7023 SettingsPageItem::SettingItem(SettingItem {
7024 title: "Play Sound When Agent Done",
7025 description: "Whether to play a sound when the agent has either completed its response, or needs user input.",
7026 field: Box::new(SettingField {
7027 json_path: Some("agent.play_sound_when_agent_done"),
7028 pick: |settings_content| {
7029 settings_content
7030 .agent
7031 .as_ref()?
7032 .play_sound_when_agent_done
7033 .as_ref()
7034 },
7035 write: |settings_content, value| {
7036 settings_content
7037 .agent
7038 .get_or_insert_default()
7039 .play_sound_when_agent_done = value;
7040 },
7041 }),
7042 metadata: None,
7043 files: USER,
7044 }),
7045 SettingsPageItem::SettingItem(SettingItem {
7046 title: "Expand Edit Card",
7047 description: "Whether to have edit cards in the agent panel expanded, showing a Preview of the diff.",
7048 field: Box::new(SettingField {
7049 json_path: Some("agent.expand_edit_card"),
7050 pick: |settings_content| {
7051 settings_content.agent.as_ref()?.expand_edit_card.as_ref()
7052 },
7053 write: |settings_content, value| {
7054 settings_content
7055 .agent
7056 .get_or_insert_default()
7057 .expand_edit_card = value;
7058 },
7059 }),
7060 metadata: None,
7061 files: USER,
7062 }),
7063 SettingsPageItem::SettingItem(SettingItem {
7064 title: "Expand Terminal Card",
7065 description: "Whether to have terminal cards in the agent panel expanded, showing the whole command output.",
7066 field: Box::new(SettingField {
7067 json_path: Some("agent.expand_terminal_card"),
7068 pick: |settings_content| {
7069 settings_content
7070 .agent
7071 .as_ref()?
7072 .expand_terminal_card
7073 .as_ref()
7074 },
7075 write: |settings_content, value| {
7076 settings_content
7077 .agent
7078 .get_or_insert_default()
7079 .expand_terminal_card = value;
7080 },
7081 }),
7082 metadata: None,
7083 files: USER,
7084 }),
7085 SettingsPageItem::SettingItem(SettingItem {
7086 title: "Cancel Generation On Terminal Stop",
7087 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.",
7088 field: Box::new(SettingField {
7089 json_path: Some("agent.cancel_generation_on_terminal_stop"),
7090 pick: |settings_content| {
7091 settings_content
7092 .agent
7093 .as_ref()?
7094 .cancel_generation_on_terminal_stop
7095 .as_ref()
7096 },
7097 write: |settings_content, value| {
7098 settings_content
7099 .agent
7100 .get_or_insert_default()
7101 .cancel_generation_on_terminal_stop = value;
7102 },
7103 }),
7104 metadata: None,
7105 files: USER,
7106 }),
7107 SettingsPageItem::SettingItem(SettingItem {
7108 title: "Use Modifier To Send",
7109 description: "Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages.",
7110 field: Box::new(SettingField {
7111 json_path: Some("agent.use_modifier_to_send"),
7112 pick: |settings_content| {
7113 settings_content
7114 .agent
7115 .as_ref()?
7116 .use_modifier_to_send
7117 .as_ref()
7118 },
7119 write: |settings_content, value| {
7120 settings_content
7121 .agent
7122 .get_or_insert_default()
7123 .use_modifier_to_send = value;
7124 },
7125 }),
7126 metadata: None,
7127 files: USER,
7128 }),
7129 SettingsPageItem::SettingItem(SettingItem {
7130 title: "Message Editor Min Lines",
7131 description: "Minimum number of lines to display in the agent message editor.",
7132 field: Box::new(SettingField {
7133 json_path: Some("agent.message_editor_min_lines"),
7134 pick: |settings_content| {
7135 settings_content
7136 .agent
7137 .as_ref()?
7138 .message_editor_min_lines
7139 .as_ref()
7140 },
7141 write: |settings_content, value| {
7142 settings_content
7143 .agent
7144 .get_or_insert_default()
7145 .message_editor_min_lines = value;
7146 },
7147 }),
7148 metadata: None,
7149 files: USER,
7150 }),
7151 SettingsPageItem::SettingItem(SettingItem {
7152 title: "Show Turn Stats",
7153 description: "Whether to show turn statistics like elapsed time during generation and final turn duration.",
7154 field: Box::new(SettingField {
7155 json_path: Some("agent.show_turn_stats"),
7156 pick: |settings_content| {
7157 settings_content.agent.as_ref()?.show_turn_stats.as_ref()
7158 },
7159 write: |settings_content, value| {
7160 settings_content
7161 .agent
7162 .get_or_insert_default()
7163 .show_turn_stats = value;
7164 },
7165 }),
7166 metadata: None,
7167 files: USER,
7168 }),
7169 ]
7170 }
7171
7172 fn context_servers_section() -> [SettingsPageItem; 2] {
7173 [
7174 SettingsPageItem::SectionHeader("Context Servers"),
7175 SettingsPageItem::SettingItem(SettingItem {
7176 title: "Context Server Timeout",
7177 description: "Default timeout in seconds for context server tool calls. Can be overridden per-server in context_servers configuration.",
7178 field: Box::new(SettingField {
7179 json_path: Some("context_server_timeout"),
7180 pick: |settings_content| {
7181 settings_content.project.context_server_timeout.as_ref()
7182 },
7183 write: |settings_content, value| {
7184 settings_content.project.context_server_timeout = value;
7185 },
7186 }),
7187 metadata: None,
7188 files: USER | PROJECT,
7189 }),
7190 ]
7191 }
7192
7193 fn edit_prediction_display_sub_section() -> [SettingsPageItem; 2] {
7194 [
7195 SettingsPageItem::SettingItem(SettingItem {
7196 title: "Display Mode",
7197 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.",
7198 field: Box::new(SettingField {
7199 json_path: Some("edit_prediction.display_mode"),
7200 pick: |settings_content| {
7201 settings_content
7202 .project
7203 .all_languages
7204 .edit_predictions
7205 .as_ref()?
7206 .mode
7207 .as_ref()
7208 },
7209 write: |settings_content, value| {
7210 settings_content
7211 .project
7212 .all_languages
7213 .edit_predictions
7214 .get_or_insert_default()
7215 .mode = value;
7216 },
7217 }),
7218 metadata: None,
7219 files: USER,
7220 }),
7221 SettingsPageItem::SettingItem(SettingItem {
7222 title: "Display In Text Threads",
7223 description: "Whether edit predictions are enabled when editing text threads in the agent panel.",
7224 field: Box::new(SettingField {
7225 json_path: Some("edit_prediction.in_text_threads"),
7226 pick: |settings_content| {
7227 settings_content
7228 .project
7229 .all_languages
7230 .edit_predictions
7231 .as_ref()?
7232 .enabled_in_text_threads
7233 .as_ref()
7234 },
7235 write: |settings_content, value| {
7236 settings_content
7237 .project
7238 .all_languages
7239 .edit_predictions
7240 .get_or_insert_default()
7241 .enabled_in_text_threads = value;
7242 },
7243 }),
7244 metadata: None,
7245 files: USER,
7246 }),
7247 ]
7248 }
7249
7250 SettingsPage {
7251 title: "AI",
7252 items: concat_sections![
7253 general_section(),
7254 agent_configuration_section(),
7255 context_servers_section(),
7256 edit_prediction_language_settings_section(),
7257 edit_prediction_display_sub_section()
7258 ],
7259 }
7260}
7261
7262fn network_page() -> SettingsPage {
7263 fn network_section() -> [SettingsPageItem; 3] {
7264 [
7265 SettingsPageItem::SectionHeader("Network"),
7266 SettingsPageItem::SettingItem(SettingItem {
7267 title: "Proxy",
7268 description: "The proxy to use for network requests.",
7269 field: Box::new(SettingField {
7270 json_path: Some("proxy"),
7271 pick: |settings_content| settings_content.proxy.as_ref(),
7272 write: |settings_content, value| {
7273 settings_content.proxy = value;
7274 },
7275 }),
7276 metadata: Some(Box::new(SettingsFieldMetadata {
7277 placeholder: Some("socks5h://localhost:10808"),
7278 ..Default::default()
7279 })),
7280 files: USER,
7281 }),
7282 SettingsPageItem::SettingItem(SettingItem {
7283 title: "Server URL",
7284 description: "The URL of the Zed server to connect to.",
7285 field: Box::new(SettingField {
7286 json_path: Some("server_url"),
7287 pick: |settings_content| settings_content.server_url.as_ref(),
7288 write: |settings_content, value| {
7289 settings_content.server_url = value;
7290 },
7291 }),
7292 metadata: Some(Box::new(SettingsFieldMetadata {
7293 placeholder: Some("https://zed.dev"),
7294 ..Default::default()
7295 })),
7296 files: USER,
7297 }),
7298 ]
7299 }
7300
7301 SettingsPage {
7302 title: "Network",
7303 items: concat_sections![network_section()],
7304 }
7305}
7306
7307fn language_settings_field<T>(
7308 settings_content: &SettingsContent,
7309 get_language_setting_field: fn(&LanguageSettingsContent) -> Option<&T>,
7310) -> Option<&T> {
7311 let all_languages = &settings_content.project.all_languages;
7312
7313 active_language()
7314 .and_then(|current_language_name| {
7315 all_languages
7316 .languages
7317 .0
7318 .get(current_language_name.as_ref())
7319 })
7320 .and_then(get_language_setting_field)
7321 .or_else(|| get_language_setting_field(&all_languages.defaults))
7322}
7323
7324fn language_settings_field_mut<T>(
7325 settings_content: &mut SettingsContent,
7326 value: Option<T>,
7327 write: fn(&mut LanguageSettingsContent, Option<T>),
7328) {
7329 let all_languages = &mut settings_content.project.all_languages;
7330 let language_content = if let Some(current_language) = active_language() {
7331 all_languages
7332 .languages
7333 .0
7334 .entry(current_language.to_string())
7335 .or_default()
7336 } else {
7337 &mut all_languages.defaults
7338 };
7339 write(language_content, value);
7340}
7341
7342fn language_settings_data() -> Box<[SettingsPageItem]> {
7343 fn indentation_section() -> [SettingsPageItem; 5] {
7344 [
7345 SettingsPageItem::SectionHeader("Indentation"),
7346 SettingsPageItem::SettingItem(SettingItem {
7347 title: "Tab Size",
7348 description: "How many columns a tab should occupy.",
7349 field: Box::new(SettingField {
7350 json_path: Some("languages.$(language).tab_size"), // TODO(cameron): not JQ syntax because not URL-safe
7351 pick: |settings_content| {
7352 language_settings_field(settings_content, |language| {
7353 language.tab_size.as_ref()
7354 })
7355 },
7356 write: |settings_content, value| {
7357 language_settings_field_mut(settings_content, value, |language, value| {
7358 language.tab_size = value;
7359 })
7360 },
7361 }),
7362 metadata: None,
7363 files: USER | PROJECT,
7364 }),
7365 SettingsPageItem::SettingItem(SettingItem {
7366 title: "Hard Tabs",
7367 description: "Whether to indent lines using tab characters, as opposed to multiple spaces.",
7368 field: Box::new(SettingField {
7369 json_path: Some("languages.$(language).hard_tabs"),
7370 pick: |settings_content| {
7371 language_settings_field(settings_content, |language| {
7372 language.hard_tabs.as_ref()
7373 })
7374 },
7375 write: |settings_content, value| {
7376 language_settings_field_mut(settings_content, value, |language, value| {
7377 language.hard_tabs = value;
7378 })
7379 },
7380 }),
7381 metadata: None,
7382 files: USER | PROJECT,
7383 }),
7384 SettingsPageItem::SettingItem(SettingItem {
7385 title: "Auto Indent",
7386 description: "Whether indentation should be adjusted based on the context whilst typing.",
7387 field: Box::new(SettingField {
7388 json_path: Some("languages.$(language).auto_indent"),
7389 pick: |settings_content| {
7390 language_settings_field(settings_content, |language| {
7391 language.auto_indent.as_ref()
7392 })
7393 },
7394 write: |settings_content, value| {
7395 language_settings_field_mut(settings_content, value, |language, value| {
7396 language.auto_indent = value;
7397 })
7398 },
7399 }),
7400 metadata: None,
7401 files: USER | PROJECT,
7402 }),
7403 SettingsPageItem::SettingItem(SettingItem {
7404 title: "Auto Indent On Paste",
7405 description: "Whether indentation of pasted content should be adjusted based on the context.",
7406 field: Box::new(SettingField {
7407 json_path: Some("languages.$(language).auto_indent_on_paste"),
7408 pick: |settings_content| {
7409 language_settings_field(settings_content, |language| {
7410 language.auto_indent_on_paste.as_ref()
7411 })
7412 },
7413 write: |settings_content, value| {
7414 language_settings_field_mut(settings_content, value, |language, value| {
7415 language.auto_indent_on_paste = value;
7416 })
7417 },
7418 }),
7419 metadata: None,
7420 files: USER | PROJECT,
7421 }),
7422 ]
7423 }
7424
7425 fn wrapping_section() -> [SettingsPageItem; 6] {
7426 [
7427 SettingsPageItem::SectionHeader("Wrapping"),
7428 SettingsPageItem::SettingItem(SettingItem {
7429 title: "Soft Wrap",
7430 description: "How to soft-wrap long lines of text.",
7431 field: Box::new(SettingField {
7432 json_path: Some("languages.$(language).soft_wrap"),
7433 pick: |settings_content| {
7434 language_settings_field(settings_content, |language| {
7435 language.soft_wrap.as_ref()
7436 })
7437 },
7438 write: |settings_content, value| {
7439 language_settings_field_mut(settings_content, value, |language, value| {
7440 language.soft_wrap = value;
7441 })
7442 },
7443 }),
7444 metadata: None,
7445 files: USER | PROJECT,
7446 }),
7447 SettingsPageItem::SettingItem(SettingItem {
7448 title: "Show Wrap Guides",
7449 description: "Show wrap guides in the editor.",
7450 field: Box::new(SettingField {
7451 json_path: Some("languages.$(language).show_wrap_guides"),
7452 pick: |settings_content| {
7453 language_settings_field(settings_content, |language| {
7454 language.show_wrap_guides.as_ref()
7455 })
7456 },
7457 write: |settings_content, value| {
7458 language_settings_field_mut(settings_content, value, |language, value| {
7459 language.show_wrap_guides = value;
7460 })
7461 },
7462 }),
7463 metadata: None,
7464 files: USER | PROJECT,
7465 }),
7466 SettingsPageItem::SettingItem(SettingItem {
7467 title: "Preferred Line Length",
7468 description: "The column at which to soft-wrap lines, for buffers where soft-wrap is enabled.",
7469 field: Box::new(SettingField {
7470 json_path: Some("languages.$(language).preferred_line_length"),
7471 pick: |settings_content| {
7472 language_settings_field(settings_content, |language| {
7473 language.preferred_line_length.as_ref()
7474 })
7475 },
7476 write: |settings_content, value| {
7477 language_settings_field_mut(settings_content, value, |language, value| {
7478 language.preferred_line_length = value;
7479 })
7480 },
7481 }),
7482 metadata: None,
7483 files: USER | PROJECT,
7484 }),
7485 SettingsPageItem::SettingItem(SettingItem {
7486 title: "Wrap Guides",
7487 description: "Character counts at which to show wrap guides in the editor.",
7488 field: Box::new(
7489 SettingField {
7490 json_path: Some("languages.$(language).wrap_guides"),
7491 pick: |settings_content| {
7492 language_settings_field(settings_content, |language| {
7493 language.wrap_guides.as_ref()
7494 })
7495 },
7496 write: |settings_content, value| {
7497 language_settings_field_mut(
7498 settings_content,
7499 value,
7500 |language, value| {
7501 language.wrap_guides = value;
7502 },
7503 )
7504 },
7505 }
7506 .unimplemented(),
7507 ),
7508 metadata: None,
7509 files: USER | PROJECT,
7510 }),
7511 SettingsPageItem::SettingItem(SettingItem {
7512 title: "Allow Rewrap",
7513 description: "Controls where the `editor::rewrap` action is allowed for this language.",
7514 field: Box::new(SettingField {
7515 json_path: Some("languages.$(language).allow_rewrap"),
7516 pick: |settings_content| {
7517 language_settings_field(settings_content, |language| {
7518 language.allow_rewrap.as_ref()
7519 })
7520 },
7521 write: |settings_content, value| {
7522 language_settings_field_mut(settings_content, value, |language, value| {
7523 language.allow_rewrap = value;
7524 })
7525 },
7526 }),
7527 metadata: None,
7528 files: USER | PROJECT,
7529 }),
7530 ]
7531 }
7532
7533 fn indent_guides_section() -> [SettingsPageItem; 6] {
7534 [
7535 SettingsPageItem::SectionHeader("Indent Guides"),
7536 SettingsPageItem::SettingItem(SettingItem {
7537 title: "Enabled",
7538 description: "Display indent guides in the editor.",
7539 field: Box::new(SettingField {
7540 json_path: Some("languages.$(language).indent_guides.enabled"),
7541 pick: |settings_content| {
7542 language_settings_field(settings_content, |language| {
7543 language
7544 .indent_guides
7545 .as_ref()
7546 .and_then(|indent_guides| indent_guides.enabled.as_ref())
7547 })
7548 },
7549 write: |settings_content, value| {
7550 language_settings_field_mut(settings_content, value, |language, value| {
7551 language.indent_guides.get_or_insert_default().enabled = value;
7552 })
7553 },
7554 }),
7555 metadata: None,
7556 files: USER | PROJECT,
7557 }),
7558 SettingsPageItem::SettingItem(SettingItem {
7559 title: "Line Width",
7560 description: "The width of the indent guides in pixels, between 1 and 10.",
7561 field: Box::new(SettingField {
7562 json_path: Some("languages.$(language).indent_guides.line_width"),
7563 pick: |settings_content| {
7564 language_settings_field(settings_content, |language| {
7565 language
7566 .indent_guides
7567 .as_ref()
7568 .and_then(|indent_guides| indent_guides.line_width.as_ref())
7569 })
7570 },
7571 write: |settings_content, value| {
7572 language_settings_field_mut(settings_content, value, |language, value| {
7573 language.indent_guides.get_or_insert_default().line_width = value;
7574 })
7575 },
7576 }),
7577 metadata: None,
7578 files: USER | PROJECT,
7579 }),
7580 SettingsPageItem::SettingItem(SettingItem {
7581 title: "Active Line Width",
7582 description: "The width of the active indent guide in pixels, between 1 and 10.",
7583 field: Box::new(SettingField {
7584 json_path: Some("languages.$(language).indent_guides.active_line_width"),
7585 pick: |settings_content| {
7586 language_settings_field(settings_content, |language| {
7587 language
7588 .indent_guides
7589 .as_ref()
7590 .and_then(|indent_guides| indent_guides.active_line_width.as_ref())
7591 })
7592 },
7593 write: |settings_content, value| {
7594 language_settings_field_mut(settings_content, value, |language, value| {
7595 language
7596 .indent_guides
7597 .get_or_insert_default()
7598 .active_line_width = value;
7599 })
7600 },
7601 }),
7602 metadata: None,
7603 files: USER | PROJECT,
7604 }),
7605 SettingsPageItem::SettingItem(SettingItem {
7606 title: "Coloring",
7607 description: "Determines how indent guides are colored.",
7608 field: Box::new(SettingField {
7609 json_path: Some("languages.$(language).indent_guides.coloring"),
7610 pick: |settings_content| {
7611 language_settings_field(settings_content, |language| {
7612 language
7613 .indent_guides
7614 .as_ref()
7615 .and_then(|indent_guides| indent_guides.coloring.as_ref())
7616 })
7617 },
7618 write: |settings_content, value| {
7619 language_settings_field_mut(settings_content, value, |language, value| {
7620 language.indent_guides.get_or_insert_default().coloring = value;
7621 })
7622 },
7623 }),
7624 metadata: None,
7625 files: USER | PROJECT,
7626 }),
7627 SettingsPageItem::SettingItem(SettingItem {
7628 title: "Background Coloring",
7629 description: "Determines how indent guide backgrounds are colored.",
7630 field: Box::new(SettingField {
7631 json_path: Some("languages.$(language).indent_guides.background_coloring"),
7632 pick: |settings_content| {
7633 language_settings_field(settings_content, |language| {
7634 language.indent_guides.as_ref().and_then(|indent_guides| {
7635 indent_guides.background_coloring.as_ref()
7636 })
7637 })
7638 },
7639 write: |settings_content, value| {
7640 language_settings_field_mut(settings_content, value, |language, value| {
7641 language
7642 .indent_guides
7643 .get_or_insert_default()
7644 .background_coloring = value;
7645 })
7646 },
7647 }),
7648 metadata: None,
7649 files: USER | PROJECT,
7650 }),
7651 ]
7652 }
7653
7654 fn formatting_section() -> [SettingsPageItem; 7] {
7655 [
7656 SettingsPageItem::SectionHeader("Formatting"),
7657 SettingsPageItem::SettingItem(SettingItem {
7658 title: "Format On Save",
7659 description: "Whether or not to perform a buffer format before saving.",
7660 field: Box::new(
7661 // TODO(settings_ui): this setting should just be a bool
7662 SettingField {
7663 json_path: Some("languages.$(language).format_on_save"),
7664 pick: |settings_content| {
7665 language_settings_field(settings_content, |language| {
7666 language.format_on_save.as_ref()
7667 })
7668 },
7669 write: |settings_content, value| {
7670 language_settings_field_mut(
7671 settings_content,
7672 value,
7673 |language, value| {
7674 language.format_on_save = value;
7675 },
7676 )
7677 },
7678 },
7679 ),
7680 metadata: None,
7681 files: USER | PROJECT,
7682 }),
7683 SettingsPageItem::SettingItem(SettingItem {
7684 title: "Remove Trailing Whitespace On Save",
7685 description: "Whether or not to remove any trailing whitespace from lines of a buffer before saving it.",
7686 field: Box::new(SettingField {
7687 json_path: Some("languages.$(language).remove_trailing_whitespace_on_save"),
7688 pick: |settings_content| {
7689 language_settings_field(settings_content, |language| {
7690 language.remove_trailing_whitespace_on_save.as_ref()
7691 })
7692 },
7693 write: |settings_content, value| {
7694 language_settings_field_mut(settings_content, value, |language, value| {
7695 language.remove_trailing_whitespace_on_save = value;
7696 })
7697 },
7698 }),
7699 metadata: None,
7700 files: USER | PROJECT,
7701 }),
7702 SettingsPageItem::SettingItem(SettingItem {
7703 title: "Ensure Final Newline On Save",
7704 description: "Whether or not to ensure there's a single newline at the end of a buffer when saving it.",
7705 field: Box::new(SettingField {
7706 json_path: Some("languages.$(language).ensure_final_newline_on_save"),
7707 pick: |settings_content| {
7708 language_settings_field(settings_content, |language| {
7709 language.ensure_final_newline_on_save.as_ref()
7710 })
7711 },
7712 write: |settings_content, value| {
7713 language_settings_field_mut(settings_content, value, |language, value| {
7714 language.ensure_final_newline_on_save = value;
7715 })
7716 },
7717 }),
7718 metadata: None,
7719 files: USER | PROJECT,
7720 }),
7721 SettingsPageItem::SettingItem(SettingItem {
7722 title: "Formatter",
7723 description: "How to perform a buffer format.",
7724 field: Box::new(
7725 SettingField {
7726 json_path: Some("languages.$(language).formatter"),
7727 pick: |settings_content| {
7728 language_settings_field(settings_content, |language| {
7729 language.formatter.as_ref()
7730 })
7731 },
7732 write: |settings_content, value| {
7733 language_settings_field_mut(
7734 settings_content,
7735 value,
7736 |language, value| {
7737 language.formatter = value;
7738 },
7739 )
7740 },
7741 }
7742 .unimplemented(),
7743 ),
7744 metadata: None,
7745 files: USER | PROJECT,
7746 }),
7747 SettingsPageItem::SettingItem(SettingItem {
7748 title: "Use On Type Format",
7749 description: "Whether to use additional LSP queries to format (and amend) the code after every \"trigger\" symbol input, defined by LSP server capabilities",
7750 field: Box::new(SettingField {
7751 json_path: Some("languages.$(language).use_on_type_format"),
7752 pick: |settings_content| {
7753 language_settings_field(settings_content, |language| {
7754 language.use_on_type_format.as_ref()
7755 })
7756 },
7757 write: |settings_content, value| {
7758 language_settings_field_mut(settings_content, value, |language, value| {
7759 language.use_on_type_format = value;
7760 })
7761 },
7762 }),
7763 metadata: None,
7764 files: USER | PROJECT,
7765 }),
7766 SettingsPageItem::SettingItem(SettingItem {
7767 title: "Code Actions On Format",
7768 description: "Additional code actions to run when formatting.",
7769 field: Box::new(
7770 SettingField {
7771 json_path: Some("languages.$(language).code_actions_on_format"),
7772 pick: |settings_content| {
7773 language_settings_field(settings_content, |language| {
7774 language.code_actions_on_format.as_ref()
7775 })
7776 },
7777 write: |settings_content, value| {
7778 language_settings_field_mut(
7779 settings_content,
7780 value,
7781 |language, value| {
7782 language.code_actions_on_format = value;
7783 },
7784 )
7785 },
7786 }
7787 .unimplemented(),
7788 ),
7789 metadata: None,
7790 files: USER | PROJECT,
7791 }),
7792 ]
7793 }
7794
7795 fn autoclose_section() -> [SettingsPageItem; 5] {
7796 [
7797 SettingsPageItem::SectionHeader("Autoclose"),
7798 SettingsPageItem::SettingItem(SettingItem {
7799 title: "Use Autoclose",
7800 description: "Whether to automatically type closing characters for you. For example, when you type '(', Zed will automatically add a closing ')' at the correct position.",
7801 field: Box::new(SettingField {
7802 json_path: Some("languages.$(language).use_autoclose"),
7803 pick: |settings_content| {
7804 language_settings_field(settings_content, |language| {
7805 language.use_autoclose.as_ref()
7806 })
7807 },
7808 write: |settings_content, value| {
7809 language_settings_field_mut(settings_content, value, |language, value| {
7810 language.use_autoclose = value;
7811 })
7812 },
7813 }),
7814 metadata: None,
7815 files: USER | PROJECT,
7816 }),
7817 SettingsPageItem::SettingItem(SettingItem {
7818 title: "Use Auto Surround",
7819 description: "Whether to automatically surround text with characters for you. For example, when you select text and type '(', Zed will automatically surround text with ().",
7820 field: Box::new(SettingField {
7821 json_path: Some("languages.$(language).use_auto_surround"),
7822 pick: |settings_content| {
7823 language_settings_field(settings_content, |language| {
7824 language.use_auto_surround.as_ref()
7825 })
7826 },
7827 write: |settings_content, value| {
7828 language_settings_field_mut(settings_content, value, |language, value| {
7829 language.use_auto_surround = value;
7830 })
7831 },
7832 }),
7833 metadata: None,
7834 files: USER | PROJECT,
7835 }),
7836 SettingsPageItem::SettingItem(SettingItem {
7837 title: "Always Treat Brackets As Autoclosed",
7838 description: "Controls whether the closing characters are always skipped over and auto-removed no matter how they were inserted.",
7839 field: Box::new(SettingField {
7840 json_path: Some("languages.$(language).always_treat_brackets_as_autoclosed"),
7841 pick: |settings_content| {
7842 language_settings_field(settings_content, |language| {
7843 language.always_treat_brackets_as_autoclosed.as_ref()
7844 })
7845 },
7846 write: |settings_content, value| {
7847 language_settings_field_mut(settings_content, value, |language, value| {
7848 language.always_treat_brackets_as_autoclosed = value;
7849 })
7850 },
7851 }),
7852 metadata: None,
7853 files: USER | PROJECT,
7854 }),
7855 SettingsPageItem::SettingItem(SettingItem {
7856 title: "JSX Tag Auto Close",
7857 description: "Whether to automatically close JSX tags.",
7858 field: Box::new(SettingField {
7859 json_path: Some("languages.$(language).jsx_tag_auto_close"),
7860 // TODO(settings_ui): this setting should just be a bool
7861 pick: |settings_content| {
7862 language_settings_field(settings_content, |language| {
7863 language.jsx_tag_auto_close.as_ref()?.enabled.as_ref()
7864 })
7865 },
7866 write: |settings_content, value| {
7867 language_settings_field_mut(settings_content, value, |language, value| {
7868 language.jsx_tag_auto_close.get_or_insert_default().enabled = value;
7869 })
7870 },
7871 }),
7872 metadata: None,
7873 files: USER | PROJECT,
7874 }),
7875 ]
7876 }
7877
7878 fn whitespace_section() -> [SettingsPageItem; 4] {
7879 [
7880 SettingsPageItem::SectionHeader("Whitespace"),
7881 SettingsPageItem::SettingItem(SettingItem {
7882 title: "Show Whitespaces",
7883 description: "Whether to show tabs and spaces in the editor.",
7884 field: Box::new(SettingField {
7885 json_path: Some("languages.$(language).show_whitespaces"),
7886 pick: |settings_content| {
7887 language_settings_field(settings_content, |language| {
7888 language.show_whitespaces.as_ref()
7889 })
7890 },
7891 write: |settings_content, value| {
7892 language_settings_field_mut(settings_content, value, |language, value| {
7893 language.show_whitespaces = value;
7894 })
7895 },
7896 }),
7897 metadata: None,
7898 files: USER | PROJECT,
7899 }),
7900 SettingsPageItem::SettingItem(SettingItem {
7901 title: "Space Whitespace Indicator",
7902 description: "Visible character used to render space characters when show_whitespaces is enabled (default: \"•\")",
7903 field: Box::new(
7904 SettingField {
7905 json_path: Some("languages.$(language).whitespace_map.space"),
7906 pick: |settings_content| {
7907 language_settings_field(settings_content, |language| {
7908 language.whitespace_map.as_ref()?.space.as_ref()
7909 })
7910 },
7911 write: |settings_content, value| {
7912 language_settings_field_mut(
7913 settings_content,
7914 value,
7915 |language, value| {
7916 language.whitespace_map.get_or_insert_default().space = value;
7917 },
7918 )
7919 },
7920 }
7921 .unimplemented(),
7922 ),
7923 metadata: None,
7924 files: USER | PROJECT,
7925 }),
7926 SettingsPageItem::SettingItem(SettingItem {
7927 title: "Tab Whitespace Indicator",
7928 description: "Visible character used to render tab characters when show_whitespaces is enabled (default: \"→\")",
7929 field: Box::new(
7930 SettingField {
7931 json_path: Some("languages.$(language).whitespace_map.tab"),
7932 pick: |settings_content| {
7933 language_settings_field(settings_content, |language| {
7934 language.whitespace_map.as_ref()?.tab.as_ref()
7935 })
7936 },
7937 write: |settings_content, value| {
7938 language_settings_field_mut(
7939 settings_content,
7940 value,
7941 |language, value| {
7942 language.whitespace_map.get_or_insert_default().tab = value;
7943 },
7944 )
7945 },
7946 }
7947 .unimplemented(),
7948 ),
7949 metadata: None,
7950 files: USER | PROJECT,
7951 }),
7952 ]
7953 }
7954
7955 fn completions_section() -> [SettingsPageItem; 7] {
7956 [
7957 SettingsPageItem::SectionHeader("Completions"),
7958 SettingsPageItem::SettingItem(SettingItem {
7959 title: "Show Completions On Input",
7960 description: "Whether to pop the completions menu while typing in an editor without explicitly requesting it.",
7961 field: Box::new(SettingField {
7962 json_path: Some("languages.$(language).show_completions_on_input"),
7963 pick: |settings_content| {
7964 language_settings_field(settings_content, |language| {
7965 language.show_completions_on_input.as_ref()
7966 })
7967 },
7968 write: |settings_content, value| {
7969 language_settings_field_mut(settings_content, value, |language, value| {
7970 language.show_completions_on_input = value;
7971 })
7972 },
7973 }),
7974 metadata: None,
7975 files: USER | PROJECT,
7976 }),
7977 SettingsPageItem::SettingItem(SettingItem {
7978 title: "Show Completion Documentation",
7979 description: "Whether to display inline and alongside documentation for items in the completions menu.",
7980 field: Box::new(SettingField {
7981 json_path: Some("languages.$(language).show_completion_documentation"),
7982 pick: |settings_content| {
7983 language_settings_field(settings_content, |language| {
7984 language.show_completion_documentation.as_ref()
7985 })
7986 },
7987 write: |settings_content, value| {
7988 language_settings_field_mut(settings_content, value, |language, value| {
7989 language.show_completion_documentation = value;
7990 })
7991 },
7992 }),
7993 metadata: None,
7994 files: USER | PROJECT,
7995 }),
7996 SettingsPageItem::SettingItem(SettingItem {
7997 title: "Words",
7998 description: "Controls how words are completed.",
7999 field: Box::new(SettingField {
8000 json_path: Some("languages.$(language).completions.words"),
8001 pick: |settings_content| {
8002 language_settings_field(settings_content, |language| {
8003 language.completions.as_ref()?.words.as_ref()
8004 })
8005 },
8006 write: |settings_content, value| {
8007 language_settings_field_mut(settings_content, value, |language, value| {
8008 language.completions.get_or_insert_default().words = value;
8009 })
8010 },
8011 }),
8012 metadata: None,
8013 files: USER | PROJECT,
8014 }),
8015 SettingsPageItem::SettingItem(SettingItem {
8016 title: "Words Min Length",
8017 description: "How many characters has to be in the completions query to automatically show the words-based completions.",
8018 field: Box::new(SettingField {
8019 json_path: Some("languages.$(language).completions.words_min_length"),
8020 pick: |settings_content| {
8021 language_settings_field(settings_content, |language| {
8022 language.completions.as_ref()?.words_min_length.as_ref()
8023 })
8024 },
8025 write: |settings_content, value| {
8026 language_settings_field_mut(settings_content, value, |language, value| {
8027 language
8028 .completions
8029 .get_or_insert_default()
8030 .words_min_length = value;
8031 })
8032 },
8033 }),
8034 metadata: None,
8035 files: USER | PROJECT,
8036 }),
8037 SettingsPageItem::SettingItem(SettingItem {
8038 title: "Completion Menu Scrollbar",
8039 description: "When to show the scrollbar in the completion menu.",
8040 field: Box::new(SettingField {
8041 json_path: Some("editor.completion_menu_scrollbar"),
8042 pick: |settings_content| {
8043 settings_content.editor.completion_menu_scrollbar.as_ref()
8044 },
8045 write: |settings_content, value| {
8046 settings_content.editor.completion_menu_scrollbar = value;
8047 },
8048 }),
8049 metadata: None,
8050 files: USER,
8051 }),
8052 SettingsPageItem::SettingItem(SettingItem {
8053 title: "Completion Detail Alignment",
8054 description: "Whether to align detail text in code completions context menus left or right.",
8055 field: Box::new(SettingField {
8056 json_path: Some("editor.completion_detail_alignment"),
8057 pick: |settings_content| {
8058 settings_content.editor.completion_detail_alignment.as_ref()
8059 },
8060 write: |settings_content, value| {
8061 settings_content.editor.completion_detail_alignment = value;
8062 },
8063 }),
8064 metadata: None,
8065 files: USER,
8066 }),
8067 ]
8068 }
8069
8070 fn inlay_hints_section() -> [SettingsPageItem; 10] {
8071 [
8072 SettingsPageItem::SectionHeader("Inlay Hints"),
8073 SettingsPageItem::SettingItem(SettingItem {
8074 title: "Enabled",
8075 description: "Global switch to toggle hints on and off.",
8076 field: Box::new(SettingField {
8077 json_path: Some("languages.$(language).inlay_hints.enabled"),
8078 pick: |settings_content| {
8079 language_settings_field(settings_content, |language| {
8080 language.inlay_hints.as_ref()?.enabled.as_ref()
8081 })
8082 },
8083 write: |settings_content, value| {
8084 language_settings_field_mut(settings_content, value, |language, value| {
8085 language.inlay_hints.get_or_insert_default().enabled = value;
8086 })
8087 },
8088 }),
8089 metadata: None,
8090 files: USER | PROJECT,
8091 }),
8092 SettingsPageItem::SettingItem(SettingItem {
8093 title: "Show Value Hints",
8094 description: "Global switch to toggle inline values on and off when debugging.",
8095 field: Box::new(SettingField {
8096 json_path: Some("languages.$(language).inlay_hints.show_value_hints"),
8097 pick: |settings_content| {
8098 language_settings_field(settings_content, |language| {
8099 language.inlay_hints.as_ref()?.show_value_hints.as_ref()
8100 })
8101 },
8102 write: |settings_content, value| {
8103 language_settings_field_mut(settings_content, value, |language, value| {
8104 language
8105 .inlay_hints
8106 .get_or_insert_default()
8107 .show_value_hints = value;
8108 })
8109 },
8110 }),
8111 metadata: None,
8112 files: USER | PROJECT,
8113 }),
8114 SettingsPageItem::SettingItem(SettingItem {
8115 title: "Show Type Hints",
8116 description: "Whether type hints should be shown.",
8117 field: Box::new(SettingField {
8118 json_path: Some("languages.$(language).inlay_hints.show_type_hints"),
8119 pick: |settings_content| {
8120 language_settings_field(settings_content, |language| {
8121 language.inlay_hints.as_ref()?.show_type_hints.as_ref()
8122 })
8123 },
8124 write: |settings_content, value| {
8125 language_settings_field_mut(settings_content, value, |language, value| {
8126 language.inlay_hints.get_or_insert_default().show_type_hints = value;
8127 })
8128 },
8129 }),
8130 metadata: None,
8131 files: USER | PROJECT,
8132 }),
8133 SettingsPageItem::SettingItem(SettingItem {
8134 title: "Show Parameter Hints",
8135 description: "Whether parameter hints should be shown.",
8136 field: Box::new(SettingField {
8137 json_path: Some("languages.$(language).inlay_hints.show_parameter_hints"),
8138 pick: |settings_content| {
8139 language_settings_field(settings_content, |language| {
8140 language.inlay_hints.as_ref()?.show_parameter_hints.as_ref()
8141 })
8142 },
8143 write: |settings_content, value| {
8144 language_settings_field_mut(settings_content, value, |language, value| {
8145 language
8146 .inlay_hints
8147 .get_or_insert_default()
8148 .show_parameter_hints = value;
8149 })
8150 },
8151 }),
8152 metadata: None,
8153 files: USER | PROJECT,
8154 }),
8155 SettingsPageItem::SettingItem(SettingItem {
8156 title: "Show Other Hints",
8157 description: "Whether other hints should be shown.",
8158 field: Box::new(SettingField {
8159 json_path: Some("languages.$(language).inlay_hints.show_other_hints"),
8160 pick: |settings_content| {
8161 language_settings_field(settings_content, |language| {
8162 language.inlay_hints.as_ref()?.show_other_hints.as_ref()
8163 })
8164 },
8165 write: |settings_content, value| {
8166 language_settings_field_mut(settings_content, value, |language, value| {
8167 language
8168 .inlay_hints
8169 .get_or_insert_default()
8170 .show_other_hints = value;
8171 })
8172 },
8173 }),
8174 metadata: None,
8175 files: USER | PROJECT,
8176 }),
8177 SettingsPageItem::SettingItem(SettingItem {
8178 title: "Show Background",
8179 description: "Show a background for inlay hints.",
8180 field: Box::new(SettingField {
8181 json_path: Some("languages.$(language).inlay_hints.show_background"),
8182 pick: |settings_content| {
8183 language_settings_field(settings_content, |language| {
8184 language.inlay_hints.as_ref()?.show_background.as_ref()
8185 })
8186 },
8187 write: |settings_content, value| {
8188 language_settings_field_mut(settings_content, value, |language, value| {
8189 language.inlay_hints.get_or_insert_default().show_background = value;
8190 })
8191 },
8192 }),
8193 metadata: None,
8194 files: USER | PROJECT,
8195 }),
8196 SettingsPageItem::SettingItem(SettingItem {
8197 title: "Edit Debounce Ms",
8198 description: "Whether or not to debounce inlay hints updates after buffer edits (set to 0 to disable debouncing).",
8199 field: Box::new(SettingField {
8200 json_path: Some("languages.$(language).inlay_hints.edit_debounce_ms"),
8201 pick: |settings_content| {
8202 language_settings_field(settings_content, |language| {
8203 language.inlay_hints.as_ref()?.edit_debounce_ms.as_ref()
8204 })
8205 },
8206 write: |settings_content, value| {
8207 language_settings_field_mut(settings_content, value, |language, value| {
8208 language
8209 .inlay_hints
8210 .get_or_insert_default()
8211 .edit_debounce_ms = value;
8212 })
8213 },
8214 }),
8215 metadata: None,
8216 files: USER | PROJECT,
8217 }),
8218 SettingsPageItem::SettingItem(SettingItem {
8219 title: "Scroll Debounce Ms",
8220 description: "Whether or not to debounce inlay hints updates after buffer scrolls (set to 0 to disable debouncing).",
8221 field: Box::new(SettingField {
8222 json_path: Some("languages.$(language).inlay_hints.scroll_debounce_ms"),
8223 pick: |settings_content| {
8224 language_settings_field(settings_content, |language| {
8225 language.inlay_hints.as_ref()?.scroll_debounce_ms.as_ref()
8226 })
8227 },
8228 write: |settings_content, value| {
8229 language_settings_field_mut(settings_content, value, |language, value| {
8230 language
8231 .inlay_hints
8232 .get_or_insert_default()
8233 .scroll_debounce_ms = value;
8234 })
8235 },
8236 }),
8237 metadata: None,
8238 files: USER | PROJECT,
8239 }),
8240 SettingsPageItem::SettingItem(SettingItem {
8241 title: "Toggle On Modifiers Press",
8242 description: "Toggles inlay hints (hides or shows) when the user presses the modifiers specified.",
8243 field: Box::new(
8244 SettingField {
8245 json_path: Some(
8246 "languages.$(language).inlay_hints.toggle_on_modifiers_press",
8247 ),
8248 pick: |settings_content| {
8249 language_settings_field(settings_content, |language| {
8250 language
8251 .inlay_hints
8252 .as_ref()?
8253 .toggle_on_modifiers_press
8254 .as_ref()
8255 })
8256 },
8257 write: |settings_content, value| {
8258 language_settings_field_mut(
8259 settings_content,
8260 value,
8261 |language, value| {
8262 language
8263 .inlay_hints
8264 .get_or_insert_default()
8265 .toggle_on_modifiers_press = value;
8266 },
8267 )
8268 },
8269 }
8270 .unimplemented(),
8271 ),
8272 metadata: None,
8273 files: USER | PROJECT,
8274 }),
8275 ]
8276 }
8277
8278 fn tasks_section() -> [SettingsPageItem; 4] {
8279 [
8280 SettingsPageItem::SectionHeader("Tasks"),
8281 SettingsPageItem::SettingItem(SettingItem {
8282 title: "Enabled",
8283 description: "Whether tasks are enabled for this language.",
8284 field: Box::new(SettingField {
8285 json_path: Some("languages.$(language).tasks.enabled"),
8286 pick: |settings_content| {
8287 language_settings_field(settings_content, |language| {
8288 language.tasks.as_ref()?.enabled.as_ref()
8289 })
8290 },
8291 write: |settings_content, value| {
8292 language_settings_field_mut(settings_content, value, |language, value| {
8293 language.tasks.get_or_insert_default().enabled = value;
8294 })
8295 },
8296 }),
8297 metadata: None,
8298 files: USER | PROJECT,
8299 }),
8300 SettingsPageItem::SettingItem(SettingItem {
8301 title: "Variables",
8302 description: "Extra task variables to set for a particular language.",
8303 field: Box::new(
8304 SettingField {
8305 json_path: Some("languages.$(language).tasks.variables"),
8306 pick: |settings_content| {
8307 language_settings_field(settings_content, |language| {
8308 language.tasks.as_ref()?.variables.as_ref()
8309 })
8310 },
8311 write: |settings_content, value| {
8312 language_settings_field_mut(
8313 settings_content,
8314 value,
8315 |language, value| {
8316 language.tasks.get_or_insert_default().variables = value;
8317 },
8318 )
8319 },
8320 }
8321 .unimplemented(),
8322 ),
8323 metadata: None,
8324 files: USER | PROJECT,
8325 }),
8326 SettingsPageItem::SettingItem(SettingItem {
8327 title: "Prefer LSP",
8328 description: "Use LSP tasks over Zed language extension tasks.",
8329 field: Box::new(SettingField {
8330 json_path: Some("languages.$(language).tasks.prefer_lsp"),
8331 pick: |settings_content| {
8332 language_settings_field(settings_content, |language| {
8333 language.tasks.as_ref()?.prefer_lsp.as_ref()
8334 })
8335 },
8336 write: |settings_content, value| {
8337 language_settings_field_mut(settings_content, value, |language, value| {
8338 language.tasks.get_or_insert_default().prefer_lsp = value;
8339 })
8340 },
8341 }),
8342 metadata: None,
8343 files: USER | PROJECT,
8344 }),
8345 ]
8346 }
8347
8348 fn miscellaneous_section() -> [SettingsPageItem; 6] {
8349 [
8350 SettingsPageItem::SectionHeader("Miscellaneous"),
8351 SettingsPageItem::SettingItem(SettingItem {
8352 title: "Word Diff Enabled",
8353 description: "Whether to enable word diff highlighting in the editor. When enabled, changed words within modified lines are highlighted to show exactly what changed.",
8354 field: Box::new(SettingField {
8355 json_path: Some("languages.$(language).word_diff_enabled"),
8356 pick: |settings_content| {
8357 language_settings_field(settings_content, |language| {
8358 language.word_diff_enabled.as_ref()
8359 })
8360 },
8361 write: |settings_content, value| {
8362 language_settings_field_mut(settings_content, value, |language, value| {
8363 language.word_diff_enabled = value;
8364 })
8365 },
8366 }),
8367 metadata: None,
8368 files: USER | PROJECT,
8369 }),
8370 SettingsPageItem::SettingItem(SettingItem {
8371 title: "Debuggers",
8372 description: "Preferred debuggers for this language.",
8373 field: Box::new(
8374 SettingField {
8375 json_path: Some("languages.$(language).debuggers"),
8376 pick: |settings_content| {
8377 language_settings_field(settings_content, |language| {
8378 language.debuggers.as_ref()
8379 })
8380 },
8381 write: |settings_content, value| {
8382 language_settings_field_mut(
8383 settings_content,
8384 value,
8385 |language, value| {
8386 language.debuggers = value;
8387 },
8388 )
8389 },
8390 }
8391 .unimplemented(),
8392 ),
8393 metadata: None,
8394 files: USER | PROJECT,
8395 }),
8396 SettingsPageItem::SettingItem(SettingItem {
8397 title: "Middle Click Paste",
8398 description: "Enable middle-click paste on Linux.",
8399 field: Box::new(SettingField {
8400 json_path: Some("languages.$(language).editor.middle_click_paste"),
8401 pick: |settings_content| settings_content.editor.middle_click_paste.as_ref(),
8402 write: |settings_content, value| {
8403 settings_content.editor.middle_click_paste = value;
8404 },
8405 }),
8406 metadata: None,
8407 files: USER,
8408 }),
8409 SettingsPageItem::SettingItem(SettingItem {
8410 title: "Extend Comment On Newline",
8411 description: "Whether to start a new line with a comment when a previous line is a comment as well.",
8412 field: Box::new(SettingField {
8413 json_path: Some("languages.$(language).extend_comment_on_newline"),
8414 pick: |settings_content| {
8415 language_settings_field(settings_content, |language| {
8416 language.extend_comment_on_newline.as_ref()
8417 })
8418 },
8419 write: |settings_content, value| {
8420 language_settings_field_mut(settings_content, value, |language, value| {
8421 language.extend_comment_on_newline = value;
8422 })
8423 },
8424 }),
8425 metadata: None,
8426 files: USER | PROJECT,
8427 }),
8428 SettingsPageItem::SettingItem(SettingItem {
8429 title: "Colorize Brackets",
8430 description: "Whether to colorize brackets in the editor.",
8431 field: Box::new(SettingField {
8432 json_path: Some("languages.$(language).colorize_brackets"),
8433 pick: |settings_content| {
8434 language_settings_field(settings_content, |language| {
8435 language.colorize_brackets.as_ref()
8436 })
8437 },
8438 write: |settings_content, value| {
8439 language_settings_field_mut(settings_content, value, |language, value| {
8440 language.colorize_brackets = value;
8441 })
8442 },
8443 }),
8444 metadata: None,
8445 files: USER | PROJECT,
8446 }),
8447 ]
8448 }
8449
8450 fn global_only_miscellaneous_sub_section() -> [SettingsPageItem; 3] {
8451 [
8452 SettingsPageItem::SettingItem(SettingItem {
8453 title: "Image Viewer",
8454 description: "The unit for image file sizes.",
8455 field: Box::new(SettingField {
8456 json_path: Some("image_viewer.unit"),
8457 pick: |settings_content| {
8458 settings_content
8459 .image_viewer
8460 .as_ref()
8461 .and_then(|image_viewer| image_viewer.unit.as_ref())
8462 },
8463 write: |settings_content, value| {
8464 settings_content.image_viewer.get_or_insert_default().unit = value;
8465 },
8466 }),
8467 metadata: None,
8468 files: USER,
8469 }),
8470 SettingsPageItem::SettingItem(SettingItem {
8471 title: "Auto Replace Emoji Shortcode",
8472 description: "Whether to automatically replace emoji shortcodes with emoji characters.",
8473 field: Box::new(SettingField {
8474 json_path: Some("message_editor.auto_replace_emoji_shortcode"),
8475 pick: |settings_content| {
8476 settings_content
8477 .message_editor
8478 .as_ref()
8479 .and_then(|message_editor| {
8480 message_editor.auto_replace_emoji_shortcode.as_ref()
8481 })
8482 },
8483 write: |settings_content, value| {
8484 settings_content
8485 .message_editor
8486 .get_or_insert_default()
8487 .auto_replace_emoji_shortcode = value;
8488 },
8489 }),
8490 metadata: None,
8491 files: USER,
8492 }),
8493 SettingsPageItem::SettingItem(SettingItem {
8494 title: "Drop Size Target",
8495 description: "Relative size of the drop target in the editor that will open dropped file as a split pane.",
8496 field: Box::new(SettingField {
8497 json_path: Some("drop_target_size"),
8498 pick: |settings_content| settings_content.workspace.drop_target_size.as_ref(),
8499 write: |settings_content, value| {
8500 settings_content.workspace.drop_target_size = value;
8501 },
8502 }),
8503 metadata: None,
8504 files: USER,
8505 }),
8506 ]
8507 }
8508
8509 let is_global = active_language().is_none();
8510
8511 let lsp_document_colors_item = [SettingsPageItem::SettingItem(SettingItem {
8512 title: "LSP Document Colors",
8513 description: "How to render LSP color previews in the editor.",
8514 field: Box::new(SettingField {
8515 json_path: Some("lsp_document_colors"),
8516 pick: |settings_content| settings_content.editor.lsp_document_colors.as_ref(),
8517 write: |settings_content, value| {
8518 settings_content.editor.lsp_document_colors = value;
8519 },
8520 }),
8521 metadata: None,
8522 files: USER,
8523 })];
8524
8525 if is_global {
8526 concat_sections!(
8527 indentation_section(),
8528 wrapping_section(),
8529 indent_guides_section(),
8530 formatting_section(),
8531 autoclose_section(),
8532 whitespace_section(),
8533 completions_section(),
8534 inlay_hints_section(),
8535 lsp_document_colors_item,
8536 tasks_section(),
8537 miscellaneous_section(),
8538 global_only_miscellaneous_sub_section(),
8539 )
8540 } else {
8541 concat_sections!(
8542 indentation_section(),
8543 wrapping_section(),
8544 indent_guides_section(),
8545 formatting_section(),
8546 autoclose_section(),
8547 whitespace_section(),
8548 completions_section(),
8549 inlay_hints_section(),
8550 tasks_section(),
8551 miscellaneous_section(),
8552 )
8553 }
8554}
8555
8556/// LanguageSettings items that should be included in the "Languages & Tools" page
8557/// not the "Editor" page
8558fn non_editor_language_settings_data() -> Box<[SettingsPageItem]> {
8559 fn lsp_section() -> [SettingsPageItem; 8] {
8560 [
8561 SettingsPageItem::SectionHeader("LSP"),
8562 SettingsPageItem::SettingItem(SettingItem {
8563 title: "Enable Language Server",
8564 description: "Whether to use language servers to provide code intelligence.",
8565 field: Box::new(SettingField {
8566 json_path: Some("languages.$(language).enable_language_server"),
8567 pick: |settings_content| {
8568 language_settings_field(settings_content, |language| {
8569 language.enable_language_server.as_ref()
8570 })
8571 },
8572 write: |settings_content, value| {
8573 language_settings_field_mut(settings_content, value, |language, value| {
8574 language.enable_language_server = value;
8575 })
8576 },
8577 }),
8578 metadata: None,
8579 files: USER | PROJECT,
8580 }),
8581 SettingsPageItem::SettingItem(SettingItem {
8582 title: "Language Servers",
8583 description: "The list of language servers to use (or disable) for this language.",
8584 field: Box::new(
8585 SettingField {
8586 json_path: Some("languages.$(language).language_servers"),
8587 pick: |settings_content| {
8588 language_settings_field(settings_content, |language| {
8589 language.language_servers.as_ref()
8590 })
8591 },
8592 write: |settings_content, value| {
8593 language_settings_field_mut(
8594 settings_content,
8595 value,
8596 |language, value| {
8597 language.language_servers = value;
8598 },
8599 )
8600 },
8601 }
8602 .unimplemented(),
8603 ),
8604 metadata: None,
8605 files: USER | PROJECT,
8606 }),
8607 SettingsPageItem::SettingItem(SettingItem {
8608 title: "Linked Edits",
8609 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.",
8610 field: Box::new(SettingField {
8611 json_path: Some("languages.$(language).linked_edits"),
8612 pick: |settings_content| {
8613 language_settings_field(settings_content, |language| {
8614 language.linked_edits.as_ref()
8615 })
8616 },
8617 write: |settings_content, value| {
8618 language_settings_field_mut(settings_content, value, |language, value| {
8619 language.linked_edits = value;
8620 })
8621 },
8622 }),
8623 metadata: None,
8624 files: USER | PROJECT,
8625 }),
8626 SettingsPageItem::SettingItem(SettingItem {
8627 title: "Go To Definition Fallback",
8628 description: "Whether to follow-up empty Go to definition responses from the language server.",
8629 field: Box::new(SettingField {
8630 json_path: Some("go_to_definition_fallback"),
8631 pick: |settings_content| {
8632 settings_content.editor.go_to_definition_fallback.as_ref()
8633 },
8634 write: |settings_content, value| {
8635 settings_content.editor.go_to_definition_fallback = value;
8636 },
8637 }),
8638 metadata: None,
8639 files: USER,
8640 }),
8641 SettingsPageItem::SettingItem(SettingItem {
8642 title: "Semantic Tokens",
8643 description: {
8644 static DESCRIPTION: OnceLock<&'static str> = OnceLock::new();
8645 DESCRIPTION.get_or_init(|| {
8646 SemanticTokens::VARIANTS
8647 .iter()
8648 .filter_map(|v| {
8649 v.get_documentation().map(|doc| format!("{v:?}: {doc}"))
8650 })
8651 .join("\n")
8652 .leak()
8653 })
8654 },
8655 field: Box::new(SettingField {
8656 json_path: Some("languages.$(language).semantic_tokens"),
8657 pick: |settings_content| {
8658 settings_content
8659 .project
8660 .all_languages
8661 .defaults
8662 .semantic_tokens
8663 .as_ref()
8664 },
8665 write: |settings_content, value| {
8666 settings_content
8667 .project
8668 .all_languages
8669 .defaults
8670 .semantic_tokens = value;
8671 },
8672 }),
8673 metadata: None,
8674 files: USER | PROJECT,
8675 }),
8676 SettingsPageItem::SettingItem(SettingItem {
8677 title: "LSP Folding Ranges",
8678 description: "When enabled, use folding ranges from the language server instead of indent-based folding.",
8679 field: Box::new(SettingField {
8680 json_path: Some("languages.$(language).document_folding_ranges"),
8681 pick: |settings_content| {
8682 language_settings_field(settings_content, |language| {
8683 language.document_folding_ranges.as_ref()
8684 })
8685 },
8686 write: |settings_content, value| {
8687 language_settings_field_mut(settings_content, value, |language, value| {
8688 language.document_folding_ranges = value;
8689 })
8690 },
8691 }),
8692 metadata: None,
8693 files: USER | PROJECT,
8694 }),
8695 SettingsPageItem::SettingItem(SettingItem {
8696 title: "LSP Document Symbols",
8697 description: "When enabled, use the language server's document symbols for outlines and breadcrumbs instead of tree-sitter.",
8698 field: Box::new(SettingField {
8699 json_path: Some("languages.$(language).document_symbols"),
8700 pick: |settings_content| {
8701 language_settings_field(settings_content, |language| {
8702 language.document_symbols.as_ref()
8703 })
8704 },
8705 write: |settings_content, value| {
8706 language_settings_field_mut(settings_content, value, |language, value| {
8707 language.document_symbols = value;
8708 })
8709 },
8710 }),
8711 metadata: None,
8712 files: USER | PROJECT,
8713 }),
8714 ]
8715 }
8716
8717 fn lsp_completions_section() -> [SettingsPageItem; 4] {
8718 [
8719 SettingsPageItem::SectionHeader("LSP Completions"),
8720 SettingsPageItem::SettingItem(SettingItem {
8721 title: "Enabled",
8722 description: "Whether to fetch LSP completions or not.",
8723 field: Box::new(SettingField {
8724 json_path: Some("languages.$(language).completions.lsp"),
8725 pick: |settings_content| {
8726 language_settings_field(settings_content, |language| {
8727 language.completions.as_ref()?.lsp.as_ref()
8728 })
8729 },
8730 write: |settings_content, value| {
8731 language_settings_field_mut(settings_content, value, |language, value| {
8732 language.completions.get_or_insert_default().lsp = value;
8733 })
8734 },
8735 }),
8736 metadata: None,
8737 files: USER | PROJECT,
8738 }),
8739 SettingsPageItem::SettingItem(SettingItem {
8740 title: "Fetch Timeout (milliseconds)",
8741 description: "When fetching LSP completions, determines how long to wait for a response of a particular server (set to 0 to wait indefinitely).",
8742 field: Box::new(SettingField {
8743 json_path: Some("languages.$(language).completions.lsp_fetch_timeout_ms"),
8744 pick: |settings_content| {
8745 language_settings_field(settings_content, |language| {
8746 language.completions.as_ref()?.lsp_fetch_timeout_ms.as_ref()
8747 })
8748 },
8749 write: |settings_content, value| {
8750 language_settings_field_mut(settings_content, value, |language, value| {
8751 language
8752 .completions
8753 .get_or_insert_default()
8754 .lsp_fetch_timeout_ms = value;
8755 })
8756 },
8757 }),
8758 metadata: None,
8759 files: USER | PROJECT,
8760 }),
8761 SettingsPageItem::SettingItem(SettingItem {
8762 title: "Insert Mode",
8763 description: "Controls how LSP completions are inserted.",
8764 field: Box::new(SettingField {
8765 json_path: Some("languages.$(language).completions.lsp_insert_mode"),
8766 pick: |settings_content| {
8767 language_settings_field(settings_content, |language| {
8768 language.completions.as_ref()?.lsp_insert_mode.as_ref()
8769 })
8770 },
8771 write: |settings_content, value| {
8772 language_settings_field_mut(settings_content, value, |language, value| {
8773 language.completions.get_or_insert_default().lsp_insert_mode = value;
8774 })
8775 },
8776 }),
8777 metadata: None,
8778 files: USER | PROJECT,
8779 }),
8780 ]
8781 }
8782
8783 fn debugger_section() -> [SettingsPageItem; 2] {
8784 [
8785 SettingsPageItem::SectionHeader("Debuggers"),
8786 SettingsPageItem::SettingItem(SettingItem {
8787 title: "Debuggers",
8788 description: "Preferred debuggers for this language.",
8789 field: Box::new(
8790 SettingField {
8791 json_path: Some("languages.$(language).debuggers"),
8792 pick: |settings_content| {
8793 language_settings_field(settings_content, |language| {
8794 language.debuggers.as_ref()
8795 })
8796 },
8797 write: |settings_content, value| {
8798 language_settings_field_mut(
8799 settings_content,
8800 value,
8801 |language, value| {
8802 language.debuggers = value;
8803 },
8804 )
8805 },
8806 }
8807 .unimplemented(),
8808 ),
8809 metadata: None,
8810 files: USER | PROJECT,
8811 }),
8812 ]
8813 }
8814
8815 fn prettier_section() -> [SettingsPageItem; 5] {
8816 [
8817 SettingsPageItem::SectionHeader("Prettier"),
8818 SettingsPageItem::SettingItem(SettingItem {
8819 title: "Allowed",
8820 description: "Enables or disables formatting with Prettier for a given language.",
8821 field: Box::new(SettingField {
8822 json_path: Some("languages.$(language).prettier.allowed"),
8823 pick: |settings_content| {
8824 language_settings_field(settings_content, |language| {
8825 language.prettier.as_ref()?.allowed.as_ref()
8826 })
8827 },
8828 write: |settings_content, value| {
8829 language_settings_field_mut(settings_content, value, |language, value| {
8830 language.prettier.get_or_insert_default().allowed = value;
8831 })
8832 },
8833 }),
8834 metadata: None,
8835 files: USER | PROJECT,
8836 }),
8837 SettingsPageItem::SettingItem(SettingItem {
8838 title: "Parser",
8839 description: "Forces Prettier integration to use a specific parser name when formatting files with the language.",
8840 field: Box::new(SettingField {
8841 json_path: Some("languages.$(language).prettier.parser"),
8842 pick: |settings_content| {
8843 language_settings_field(settings_content, |language| {
8844 language.prettier.as_ref()?.parser.as_ref()
8845 })
8846 },
8847 write: |settings_content, value| {
8848 language_settings_field_mut(settings_content, value, |language, value| {
8849 language.prettier.get_or_insert_default().parser = value;
8850 })
8851 },
8852 }),
8853 metadata: None,
8854 files: USER | PROJECT,
8855 }),
8856 SettingsPageItem::SettingItem(SettingItem {
8857 title: "Plugins",
8858 description: "Forces Prettier integration to use specific plugins when formatting files with the language.",
8859 field: Box::new(
8860 SettingField {
8861 json_path: Some("languages.$(language).prettier.plugins"),
8862 pick: |settings_content| {
8863 language_settings_field(settings_content, |language| {
8864 language.prettier.as_ref()?.plugins.as_ref()
8865 })
8866 },
8867 write: |settings_content, value| {
8868 language_settings_field_mut(
8869 settings_content,
8870 value,
8871 |language, value| {
8872 language.prettier.get_or_insert_default().plugins = value;
8873 },
8874 )
8875 },
8876 }
8877 .unimplemented(),
8878 ),
8879 metadata: None,
8880 files: USER | PROJECT,
8881 }),
8882 SettingsPageItem::SettingItem(SettingItem {
8883 title: "Options",
8884 description: "Default Prettier options, in the format as in package.json section for Prettier.",
8885 field: Box::new(
8886 SettingField {
8887 json_path: Some("languages.$(language).prettier.options"),
8888 pick: |settings_content| {
8889 language_settings_field(settings_content, |language| {
8890 language.prettier.as_ref()?.options.as_ref()
8891 })
8892 },
8893 write: |settings_content, value| {
8894 language_settings_field_mut(
8895 settings_content,
8896 value,
8897 |language, value| {
8898 language.prettier.get_or_insert_default().options = value;
8899 },
8900 )
8901 },
8902 }
8903 .unimplemented(),
8904 ),
8905 metadata: None,
8906 files: USER | PROJECT,
8907 }),
8908 ]
8909 }
8910
8911 concat_sections!(
8912 lsp_section(),
8913 lsp_completions_section(),
8914 debugger_section(),
8915 prettier_section(),
8916 )
8917}
8918
8919fn edit_prediction_language_settings_section() -> [SettingsPageItem; 4] {
8920 [
8921 SettingsPageItem::SectionHeader("Edit Predictions"),
8922 SettingsPageItem::SubPageLink(SubPageLink {
8923 title: "Configure Providers".into(),
8924 r#type: Default::default(),
8925 json_path: Some("edit_predictions.providers"),
8926 description: Some("Set up different edit prediction providers in complement to Zed's built-in Zeta model.".into()),
8927 in_json: false,
8928 files: USER,
8929 render: render_edit_prediction_setup_page
8930 }),
8931 SettingsPageItem::SettingItem(SettingItem {
8932 title: "Show Edit Predictions",
8933 description: "Controls whether edit predictions are shown immediately or manually.",
8934 field: Box::new(SettingField {
8935 json_path: Some("languages.$(language).show_edit_predictions"),
8936 pick: |settings_content| {
8937 language_settings_field(settings_content, |language| {
8938 language.show_edit_predictions.as_ref()
8939 })
8940 },
8941 write: |settings_content, value| {
8942 language_settings_field_mut(settings_content, value, |language, value| {
8943 language.show_edit_predictions = value;
8944 })
8945 },
8946 }),
8947 metadata: None,
8948 files: USER | PROJECT,
8949 }),
8950 SettingsPageItem::SettingItem(SettingItem {
8951 title: "Disable in Language Scopes",
8952 description: "Controls whether edit predictions are shown in the given language scopes.",
8953 field: Box::new(
8954 SettingField {
8955 json_path: Some("languages.$(language).edit_predictions_disabled_in"),
8956 pick: |settings_content| {
8957 language_settings_field(settings_content, |language| {
8958 language.edit_predictions_disabled_in.as_ref()
8959 })
8960 },
8961 write: |settings_content, value| {
8962 language_settings_field_mut(settings_content, value, |language, value| {
8963 language.edit_predictions_disabled_in = value;
8964 })
8965 },
8966 }
8967 .unimplemented(),
8968 ),
8969 metadata: None,
8970 files: USER | PROJECT,
8971 }),
8972 ]
8973}
8974
8975fn show_scrollbar_or_editor(
8976 settings_content: &SettingsContent,
8977 show: fn(&SettingsContent) -> Option<&settings::ShowScrollbar>,
8978) -> Option<&settings::ShowScrollbar> {
8979 show(settings_content).or(settings_content
8980 .editor
8981 .scrollbar
8982 .as_ref()
8983 .and_then(|scrollbar| scrollbar.show.as_ref()))
8984}
8985
8986fn dynamic_variants<T>() -> &'static [T::Discriminant]
8987where
8988 T: strum::IntoDiscriminant,
8989 T::Discriminant: strum::VariantArray,
8990{
8991 <<T as strum::IntoDiscriminant>::Discriminant as strum::VariantArray>::VARIANTS
8992}