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