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