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 notification_panel_section() -> [SettingsPageItem; 5] {
5583 [
5584 SettingsPageItem::SectionHeader("Notification Panel"),
5585 SettingsPageItem::SettingItem(SettingItem {
5586 title: "Notification Panel Button",
5587 description: "Show the notification panel button in the status bar.",
5588 field: Box::new(SettingField {
5589 json_path: Some("notification_panel.button"),
5590 pick: |settings_content| {
5591 settings_content
5592 .notification_panel
5593 .as_ref()?
5594 .button
5595 .as_ref()
5596 },
5597 write: |settings_content, value| {
5598 settings_content
5599 .notification_panel
5600 .get_or_insert_default()
5601 .button = value;
5602 },
5603 }),
5604 metadata: None,
5605 files: USER,
5606 }),
5607 SettingsPageItem::SettingItem(SettingItem {
5608 title: "Notification Panel Dock",
5609 description: "Where to dock the notification panel.",
5610 field: Box::new(SettingField {
5611 json_path: Some("notification_panel.dock"),
5612 pick: |settings_content| {
5613 settings_content.notification_panel.as_ref()?.dock.as_ref()
5614 },
5615 write: |settings_content, value| {
5616 settings_content
5617 .notification_panel
5618 .get_or_insert_default()
5619 .dock = value;
5620 },
5621 }),
5622 metadata: None,
5623 files: USER,
5624 }),
5625 SettingsPageItem::SettingItem(SettingItem {
5626 title: "Notification Panel Default Width",
5627 description: "Default width of the notification panel in pixels.",
5628 field: Box::new(SettingField {
5629 json_path: Some("notification_panel.default_width"),
5630 pick: |settings_content| {
5631 settings_content
5632 .notification_panel
5633 .as_ref()?
5634 .default_width
5635 .as_ref()
5636 },
5637 write: |settings_content, value| {
5638 settings_content
5639 .notification_panel
5640 .get_or_insert_default()
5641 .default_width = value;
5642 },
5643 }),
5644 metadata: None,
5645 files: USER,
5646 }),
5647 SettingsPageItem::SettingItem(SettingItem {
5648 title: "Show Count Badge",
5649 description: "Show a badge on the notification panel icon with the count of unread notifications.",
5650 field: Box::new(SettingField {
5651 json_path: Some("notification_panel.show_count_badge"),
5652 pick: |settings_content| {
5653 settings_content
5654 .notification_panel
5655 .as_ref()?
5656 .show_count_badge
5657 .as_ref()
5658 },
5659 write: |settings_content, value| {
5660 settings_content
5661 .notification_panel
5662 .get_or_insert_default()
5663 .show_count_badge = value;
5664 },
5665 }),
5666 metadata: None,
5667 files: USER,
5668 }),
5669 ]
5670 }
5671
5672 fn collaboration_panel_section() -> [SettingsPageItem; 4] {
5673 [
5674 SettingsPageItem::SectionHeader("Collaboration Panel"),
5675 SettingsPageItem::SettingItem(SettingItem {
5676 title: "Collaboration Panel Button",
5677 description: "Show the collaboration panel button in the status bar.",
5678 field: Box::new(SettingField {
5679 json_path: Some("collaboration_panel.button"),
5680 pick: |settings_content| {
5681 settings_content
5682 .collaboration_panel
5683 .as_ref()?
5684 .button
5685 .as_ref()
5686 },
5687 write: |settings_content, value| {
5688 settings_content
5689 .collaboration_panel
5690 .get_or_insert_default()
5691 .button = value;
5692 },
5693 }),
5694 metadata: None,
5695 files: USER,
5696 }),
5697 SettingsPageItem::SettingItem(SettingItem {
5698 title: "Collaboration Panel Dock",
5699 description: "Where to dock the collaboration panel.",
5700 field: Box::new(SettingField {
5701 json_path: Some("collaboration_panel.dock"),
5702 pick: |settings_content| {
5703 settings_content.collaboration_panel.as_ref()?.dock.as_ref()
5704 },
5705 write: |settings_content, value| {
5706 settings_content
5707 .collaboration_panel
5708 .get_or_insert_default()
5709 .dock = value;
5710 },
5711 }),
5712 metadata: None,
5713 files: USER,
5714 }),
5715 SettingsPageItem::SettingItem(SettingItem {
5716 title: "Collaboration Panel Default Width",
5717 description: "Default width of the collaboration panel in pixels.",
5718 field: Box::new(SettingField {
5719 json_path: Some("collaboration_panel.dock"),
5720 pick: |settings_content| {
5721 settings_content
5722 .collaboration_panel
5723 .as_ref()?
5724 .default_width
5725 .as_ref()
5726 },
5727 write: |settings_content, value| {
5728 settings_content
5729 .collaboration_panel
5730 .get_or_insert_default()
5731 .default_width = value;
5732 },
5733 }),
5734 metadata: None,
5735 files: USER,
5736 }),
5737 ]
5738 }
5739
5740 fn agent_panel_section() -> [SettingsPageItem; 6] {
5741 [
5742 SettingsPageItem::SectionHeader("Agent Panel"),
5743 SettingsPageItem::SettingItem(SettingItem {
5744 title: "Agent Panel Button",
5745 description: "Whether to show the agent panel button in the status bar.",
5746 field: Box::new(SettingField {
5747 json_path: Some("agent.button"),
5748 pick: |settings_content| settings_content.agent.as_ref()?.button.as_ref(),
5749 write: |settings_content, value| {
5750 settings_content.agent.get_or_insert_default().button = value;
5751 },
5752 }),
5753 metadata: None,
5754 files: USER,
5755 }),
5756 SettingsPageItem::SettingItem(SettingItem {
5757 title: "Agent Panel Dock",
5758 description: "Where to dock the agent panel.",
5759 field: Box::new(SettingField {
5760 json_path: Some("agent.dock"),
5761 pick: |settings_content| settings_content.agent.as_ref()?.dock.as_ref(),
5762 write: |settings_content, value| {
5763 settings_content.agent.get_or_insert_default().dock = value;
5764 },
5765 }),
5766 metadata: None,
5767 files: USER,
5768 }),
5769 SettingsPageItem::SettingItem(SettingItem {
5770 title: "Agent Panel Flexible Sizing",
5771 description: "Whether the agent panel should use flexible (proportional) sizing when docked to the left or right.",
5772 field: Box::new(SettingField {
5773 json_path: Some("agent.flexible"),
5774 pick: |settings_content| settings_content.agent.as_ref()?.flexible.as_ref(),
5775 write: |settings_content, value| {
5776 settings_content.agent.get_or_insert_default().flexible = value;
5777 },
5778 }),
5779 metadata: None,
5780 files: USER,
5781 }),
5782 SettingsPageItem::SettingItem(SettingItem {
5783 title: "Agent Panel Default Width",
5784 description: "Default width when the agent panel is docked to the left or right.",
5785 field: Box::new(SettingField {
5786 json_path: Some("agent.default_width"),
5787 pick: |settings_content| {
5788 settings_content.agent.as_ref()?.default_width.as_ref()
5789 },
5790 write: |settings_content, value| {
5791 settings_content.agent.get_or_insert_default().default_width = value;
5792 },
5793 }),
5794 metadata: None,
5795 files: USER,
5796 }),
5797 SettingsPageItem::SettingItem(SettingItem {
5798 title: "Agent Panel Default Height",
5799 description: "Default height when the agent panel is docked to the bottom.",
5800 field: Box::new(SettingField {
5801 json_path: Some("agent.default_height"),
5802 pick: |settings_content| {
5803 settings_content.agent.as_ref()?.default_height.as_ref()
5804 },
5805 write: |settings_content, value| {
5806 settings_content
5807 .agent
5808 .get_or_insert_default()
5809 .default_height = value;
5810 },
5811 }),
5812 metadata: None,
5813 files: USER,
5814 }),
5815 ]
5816 }
5817
5818 SettingsPage {
5819 title: "Panels",
5820 items: concat_sections![
5821 project_panel_section(),
5822 terminal_panel_section(),
5823 outline_panel_section(),
5824 git_panel_section(),
5825 debugger_panel_section(),
5826 notification_panel_section(),
5827 collaboration_panel_section(),
5828 agent_panel_section(),
5829 ],
5830 }
5831}
5832
5833fn debugger_page() -> SettingsPage {
5834 fn general_section() -> [SettingsPageItem; 6] {
5835 [
5836 SettingsPageItem::SectionHeader("General"),
5837 SettingsPageItem::SettingItem(SettingItem {
5838 title: "Stepping Granularity",
5839 description: "Determines the stepping granularity for debug operations.",
5840 field: Box::new(SettingField {
5841 json_path: Some("debugger.stepping_granularity"),
5842 pick: |settings_content| {
5843 settings_content
5844 .debugger
5845 .as_ref()?
5846 .stepping_granularity
5847 .as_ref()
5848 },
5849 write: |settings_content, value| {
5850 settings_content
5851 .debugger
5852 .get_or_insert_default()
5853 .stepping_granularity = value;
5854 },
5855 }),
5856 metadata: None,
5857 files: USER,
5858 }),
5859 SettingsPageItem::SettingItem(SettingItem {
5860 title: "Save Breakpoints",
5861 description: "Whether breakpoints should be reused across Zed sessions.",
5862 field: Box::new(SettingField {
5863 json_path: Some("debugger.save_breakpoints"),
5864 pick: |settings_content| {
5865 settings_content
5866 .debugger
5867 .as_ref()?
5868 .save_breakpoints
5869 .as_ref()
5870 },
5871 write: |settings_content, value| {
5872 settings_content
5873 .debugger
5874 .get_or_insert_default()
5875 .save_breakpoints = value;
5876 },
5877 }),
5878 metadata: None,
5879 files: USER,
5880 }),
5881 SettingsPageItem::SettingItem(SettingItem {
5882 title: "Timeout",
5883 description: "Time in milliseconds until timeout error when connecting to a TCP debug adapter.",
5884 field: Box::new(SettingField {
5885 json_path: Some("debugger.timeout"),
5886 pick: |settings_content| settings_content.debugger.as_ref()?.timeout.as_ref(),
5887 write: |settings_content, value| {
5888 settings_content.debugger.get_or_insert_default().timeout = value;
5889 },
5890 }),
5891 metadata: None,
5892 files: USER,
5893 }),
5894 SettingsPageItem::SettingItem(SettingItem {
5895 title: "Log DAP Communications",
5896 description: "Whether to log messages between active debug adapters and Zed.",
5897 field: Box::new(SettingField {
5898 json_path: Some("debugger.log_dap_communications"),
5899 pick: |settings_content| {
5900 settings_content
5901 .debugger
5902 .as_ref()?
5903 .log_dap_communications
5904 .as_ref()
5905 },
5906 write: |settings_content, value| {
5907 settings_content
5908 .debugger
5909 .get_or_insert_default()
5910 .log_dap_communications = value;
5911 },
5912 }),
5913 metadata: None,
5914 files: USER,
5915 }),
5916 SettingsPageItem::SettingItem(SettingItem {
5917 title: "Format DAP Log Messages",
5918 description: "Whether to format DAP messages when adding them to debug adapter logger.",
5919 field: Box::new(SettingField {
5920 json_path: Some("debugger.format_dap_log_messages"),
5921 pick: |settings_content| {
5922 settings_content
5923 .debugger
5924 .as_ref()?
5925 .format_dap_log_messages
5926 .as_ref()
5927 },
5928 write: |settings_content, value| {
5929 settings_content
5930 .debugger
5931 .get_or_insert_default()
5932 .format_dap_log_messages = value;
5933 },
5934 }),
5935 metadata: None,
5936 files: USER,
5937 }),
5938 ]
5939 }
5940
5941 SettingsPage {
5942 title: "Debugger",
5943 items: concat_sections![general_section()],
5944 }
5945}
5946
5947fn terminal_page() -> SettingsPage {
5948 fn environment_section() -> [SettingsPageItem; 5] {
5949 [
5950 SettingsPageItem::SectionHeader("Environment"),
5951 SettingsPageItem::DynamicItem(DynamicItem {
5952 discriminant: SettingItem {
5953 files: USER | PROJECT,
5954 title: "Shell",
5955 description: "What shell to use when opening a terminal.",
5956 field: Box::new(SettingField {
5957 json_path: Some("terminal.shell$"),
5958 pick: |settings_content| {
5959 Some(&dynamic_variants::<settings::Shell>()[
5960 settings_content
5961 .terminal
5962 .as_ref()?
5963 .project
5964 .shell
5965 .as_ref()?
5966 .discriminant() as usize
5967 ])
5968 },
5969 write: |settings_content, value| {
5970 let Some(value) = value else {
5971 if let Some(terminal) = settings_content.terminal.as_mut() {
5972 terminal.project.shell = None;
5973 }
5974 return;
5975 };
5976 let settings_value = settings_content
5977 .terminal
5978 .get_or_insert_default()
5979 .project
5980 .shell
5981 .get_or_insert_with(|| settings::Shell::default());
5982 let default_shell = if cfg!(target_os = "windows") {
5983 "powershell.exe"
5984 } else {
5985 "sh"
5986 };
5987 *settings_value = match value {
5988 settings::ShellDiscriminants::System => settings::Shell::System,
5989 settings::ShellDiscriminants::Program => {
5990 let program = match settings_value {
5991 settings::Shell::Program(program) => program.clone(),
5992 settings::Shell::WithArguments { program, .. } => program.clone(),
5993 _ => String::from(default_shell),
5994 };
5995 settings::Shell::Program(program)
5996 }
5997 settings::ShellDiscriminants::WithArguments => {
5998 let (program, args, title_override) = match settings_value {
5999 settings::Shell::Program(program) => (program.clone(), vec![], None),
6000 settings::Shell::WithArguments {
6001 program,
6002 args,
6003 title_override,
6004 } => (program.clone(), args.clone(), title_override.clone()),
6005 _ => (String::from(default_shell), vec![], None),
6006 };
6007 settings::Shell::WithArguments {
6008 program,
6009 args,
6010 title_override,
6011 }
6012 }
6013 };
6014 },
6015 }),
6016 metadata: None,
6017 },
6018 pick_discriminant: |settings_content| {
6019 Some(
6020 settings_content
6021 .terminal
6022 .as_ref()?
6023 .project
6024 .shell
6025 .as_ref()?
6026 .discriminant() as usize,
6027 )
6028 },
6029 fields: dynamic_variants::<settings::Shell>()
6030 .into_iter()
6031 .map(|variant| match variant {
6032 settings::ShellDiscriminants::System => vec![],
6033 settings::ShellDiscriminants::Program => vec![SettingItem {
6034 files: USER | PROJECT,
6035 title: "Program",
6036 description: "The shell program to use.",
6037 field: Box::new(SettingField {
6038 json_path: Some("terminal.shell"),
6039 pick: |settings_content| match settings_content.terminal.as_ref()?.project.shell.as_ref()
6040 {
6041 Some(settings::Shell::Program(program)) => Some(program),
6042 _ => None,
6043 },
6044 write: |settings_content, value| {
6045 let Some(value) = value else {
6046 return;
6047 };
6048 match settings_content
6049 .terminal
6050 .get_or_insert_default()
6051 .project
6052 .shell
6053 .as_mut()
6054 {
6055 Some(settings::Shell::Program(program)) => *program = value,
6056 _ => return,
6057 }
6058 },
6059 }),
6060 metadata: None,
6061 }],
6062 settings::ShellDiscriminants::WithArguments => vec![
6063 SettingItem {
6064 files: USER | PROJECT,
6065 title: "Program",
6066 description: "The shell program to run.",
6067 field: Box::new(SettingField {
6068 json_path: Some("terminal.shell.program"),
6069 pick: |settings_content| {
6070 match settings_content.terminal.as_ref()?.project.shell.as_ref() {
6071 Some(settings::Shell::WithArguments { program, .. }) => Some(program),
6072 _ => None,
6073 }
6074 },
6075 write: |settings_content, value| {
6076 let Some(value) = value else {
6077 return;
6078 };
6079 match settings_content
6080 .terminal
6081 .get_or_insert_default()
6082 .project
6083 .shell
6084 .as_mut()
6085 {
6086 Some(settings::Shell::WithArguments { program, .. }) => {
6087 *program = value
6088 }
6089 _ => return,
6090 }
6091 },
6092 }),
6093 metadata: None,
6094 },
6095 SettingItem {
6096 files: USER | PROJECT,
6097 title: "Arguments",
6098 description: "The arguments to pass to the shell program.",
6099 field: Box::new(
6100 SettingField {
6101 json_path: Some("terminal.shell.args"),
6102 pick: |settings_content| {
6103 match settings_content.terminal.as_ref()?.project.shell.as_ref() {
6104 Some(settings::Shell::WithArguments { args, .. }) => Some(args),
6105 _ => None,
6106 }
6107 },
6108 write: |settings_content, value| {
6109 let Some(value) = value else {
6110 return;
6111 };
6112 match settings_content
6113 .terminal
6114 .get_or_insert_default()
6115 .project
6116 .shell
6117 .as_mut()
6118 {
6119 Some(settings::Shell::WithArguments { args, .. }) => *args = value,
6120 _ => return,
6121 }
6122 },
6123 }
6124 .unimplemented(),
6125 ),
6126 metadata: None,
6127 },
6128 SettingItem {
6129 files: USER | PROJECT,
6130 title: "Title Override",
6131 description: "An optional string to override the title of the terminal tab.",
6132 field: Box::new(SettingField {
6133 json_path: Some("terminal.shell.title_override"),
6134 pick: |settings_content| {
6135 match settings_content.terminal.as_ref()?.project.shell.as_ref() {
6136 Some(settings::Shell::WithArguments { title_override, .. }) => {
6137 title_override.as_ref().or(DEFAULT_EMPTY_STRING)
6138 }
6139 _ => None,
6140 }
6141 },
6142 write: |settings_content, value| {
6143 match settings_content
6144 .terminal
6145 .get_or_insert_default()
6146 .project
6147 .shell
6148 .as_mut()
6149 {
6150 Some(settings::Shell::WithArguments { title_override, .. }) => {
6151 *title_override = value.filter(|s| !s.is_empty())
6152 }
6153 _ => return,
6154 }
6155 },
6156 }),
6157 metadata: None,
6158 },
6159 ],
6160 })
6161 .collect(),
6162 }),
6163 SettingsPageItem::DynamicItem(DynamicItem {
6164 discriminant: SettingItem {
6165 files: USER | PROJECT,
6166 title: "Working Directory",
6167 description: "What working directory to use when launching the terminal.",
6168 field: Box::new(SettingField {
6169 json_path: Some("terminal.working_directory$"),
6170 pick: |settings_content| {
6171 Some(&dynamic_variants::<settings::WorkingDirectory>()[
6172 settings_content
6173 .terminal
6174 .as_ref()?
6175 .project
6176 .working_directory
6177 .as_ref()?
6178 .discriminant() as usize
6179 ])
6180 },
6181 write: |settings_content, value| {
6182 let Some(value) = value else {
6183 if let Some(terminal) = settings_content.terminal.as_mut() {
6184 terminal.project.working_directory = None;
6185 }
6186 return;
6187 };
6188 let settings_value = settings_content
6189 .terminal
6190 .get_or_insert_default()
6191 .project
6192 .working_directory
6193 .get_or_insert_with(|| settings::WorkingDirectory::CurrentProjectDirectory);
6194 *settings_value = match value {
6195 settings::WorkingDirectoryDiscriminants::CurrentFileDirectory => {
6196 settings::WorkingDirectory::CurrentFileDirectory
6197 },
6198 settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => {
6199 settings::WorkingDirectory::CurrentProjectDirectory
6200 }
6201 settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => {
6202 settings::WorkingDirectory::FirstProjectDirectory
6203 }
6204 settings::WorkingDirectoryDiscriminants::AlwaysHome => {
6205 settings::WorkingDirectory::AlwaysHome
6206 }
6207 settings::WorkingDirectoryDiscriminants::Always => {
6208 let directory = match settings_value {
6209 settings::WorkingDirectory::Always { .. } => return,
6210 _ => String::new(),
6211 };
6212 settings::WorkingDirectory::Always { directory }
6213 }
6214 };
6215 },
6216 }),
6217 metadata: None,
6218 },
6219 pick_discriminant: |settings_content| {
6220 Some(
6221 settings_content
6222 .terminal
6223 .as_ref()?
6224 .project
6225 .working_directory
6226 .as_ref()?
6227 .discriminant() as usize,
6228 )
6229 },
6230 fields: dynamic_variants::<settings::WorkingDirectory>()
6231 .into_iter()
6232 .map(|variant| match variant {
6233 settings::WorkingDirectoryDiscriminants::CurrentFileDirectory => vec![],
6234 settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => vec![],
6235 settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => vec![],
6236 settings::WorkingDirectoryDiscriminants::AlwaysHome => vec![],
6237 settings::WorkingDirectoryDiscriminants::Always => vec![SettingItem {
6238 files: USER | PROJECT,
6239 title: "Directory",
6240 description: "The directory path to use (will be shell expanded).",
6241 field: Box::new(SettingField {
6242 json_path: Some("terminal.working_directory.always"),
6243 pick: |settings_content| {
6244 match settings_content.terminal.as_ref()?.project.working_directory.as_ref() {
6245 Some(settings::WorkingDirectory::Always { directory }) => Some(directory),
6246 _ => None,
6247 }
6248 },
6249 write: |settings_content, value| {
6250 let value = value.unwrap_or_default();
6251 match settings_content
6252 .terminal
6253 .get_or_insert_default()
6254 .project
6255 .working_directory
6256 .as_mut()
6257 {
6258 Some(settings::WorkingDirectory::Always { directory }) => *directory = value,
6259 _ => return,
6260 }
6261 },
6262 }),
6263 metadata: None,
6264 }],
6265 })
6266 .collect(),
6267 }),
6268 SettingsPageItem::SettingItem(SettingItem {
6269 title: "Environment Variables",
6270 description: "Key-value pairs to add to the terminal's environment.",
6271 field: Box::new(
6272 SettingField {
6273 json_path: Some("terminal.env"),
6274 pick: |settings_content| settings_content.terminal.as_ref()?.project.env.as_ref(),
6275 write: |settings_content, value| {
6276 settings_content.terminal.get_or_insert_default().project.env = value;
6277 },
6278 }
6279 .unimplemented(),
6280 ),
6281 metadata: None,
6282 files: USER | PROJECT,
6283 }),
6284 SettingsPageItem::SettingItem(SettingItem {
6285 title: "Detect Virtual Environment",
6286 description: "Activates the Python virtual environment, if one is found, in the terminal's working directory.",
6287 field: Box::new(
6288 SettingField {
6289 json_path: Some("terminal.detect_venv"),
6290 pick: |settings_content| settings_content.terminal.as_ref()?.project.detect_venv.as_ref(),
6291 write: |settings_content, value| {
6292 settings_content
6293 .terminal
6294 .get_or_insert_default()
6295 .project
6296 .detect_venv = value;
6297 },
6298 }
6299 .unimplemented(),
6300 ),
6301 metadata: None,
6302 files: USER | PROJECT,
6303 }),
6304 ]
6305 }
6306
6307 fn font_section() -> [SettingsPageItem; 6] {
6308 [
6309 SettingsPageItem::SectionHeader("Font"),
6310 SettingsPageItem::SettingItem(SettingItem {
6311 title: "Font Size",
6312 description: "Font size for terminal text. If not set, defaults to buffer font size.",
6313 field: Box::new(SettingField {
6314 json_path: Some("terminal.font_size"),
6315 pick: |settings_content| {
6316 settings_content
6317 .terminal
6318 .as_ref()
6319 .and_then(|terminal| terminal.font_size.as_ref())
6320 .or(settings_content.theme.buffer_font_size.as_ref())
6321 },
6322 write: |settings_content, value| {
6323 settings_content.terminal.get_or_insert_default().font_size = value;
6324 },
6325 }),
6326 metadata: None,
6327 files: USER,
6328 }),
6329 SettingsPageItem::SettingItem(SettingItem {
6330 title: "Font Family",
6331 description: "Font family for terminal text. If not set, defaults to buffer font family.",
6332 field: Box::new(SettingField {
6333 json_path: Some("terminal.font_family"),
6334 pick: |settings_content| {
6335 settings_content
6336 .terminal
6337 .as_ref()
6338 .and_then(|terminal| terminal.font_family.as_ref())
6339 .or(settings_content.theme.buffer_font_family.as_ref())
6340 },
6341 write: |settings_content, value| {
6342 settings_content
6343 .terminal
6344 .get_or_insert_default()
6345 .font_family = value;
6346 },
6347 }),
6348 metadata: None,
6349 files: USER,
6350 }),
6351 SettingsPageItem::SettingItem(SettingItem {
6352 title: "Font Fallbacks",
6353 description: "Font fallbacks for terminal text. If not set, defaults to buffer font fallbacks.",
6354 field: Box::new(
6355 SettingField {
6356 json_path: Some("terminal.font_fallbacks"),
6357 pick: |settings_content| {
6358 settings_content
6359 .terminal
6360 .as_ref()
6361 .and_then(|terminal| terminal.font_fallbacks.as_ref())
6362 .or(settings_content.theme.buffer_font_fallbacks.as_ref())
6363 },
6364 write: |settings_content, value| {
6365 settings_content
6366 .terminal
6367 .get_or_insert_default()
6368 .font_fallbacks = value;
6369 },
6370 }
6371 .unimplemented(),
6372 ),
6373 metadata: None,
6374 files: USER,
6375 }),
6376 SettingsPageItem::SettingItem(SettingItem {
6377 title: "Font Weight",
6378 description: "Font weight for terminal text in CSS weight units (100-900).",
6379 field: Box::new(SettingField {
6380 json_path: Some("terminal.font_weight"),
6381 pick: |settings_content| {
6382 settings_content.terminal.as_ref()?.font_weight.as_ref()
6383 },
6384 write: |settings_content, value| {
6385 settings_content
6386 .terminal
6387 .get_or_insert_default()
6388 .font_weight = value;
6389 },
6390 }),
6391 metadata: None,
6392 files: USER,
6393 }),
6394 SettingsPageItem::SettingItem(SettingItem {
6395 title: "Font Features",
6396 description: "Font features for terminal text.",
6397 field: Box::new(
6398 SettingField {
6399 json_path: Some("terminal.font_features"),
6400 pick: |settings_content| {
6401 settings_content
6402 .terminal
6403 .as_ref()
6404 .and_then(|terminal| terminal.font_features.as_ref())
6405 .or(settings_content.theme.buffer_font_features.as_ref())
6406 },
6407 write: |settings_content, value| {
6408 settings_content
6409 .terminal
6410 .get_or_insert_default()
6411 .font_features = value;
6412 },
6413 }
6414 .unimplemented(),
6415 ),
6416 metadata: None,
6417 files: USER,
6418 }),
6419 ]
6420 }
6421
6422 fn display_settings_section() -> [SettingsPageItem; 6] {
6423 [
6424 SettingsPageItem::SectionHeader("Display Settings"),
6425 SettingsPageItem::SettingItem(SettingItem {
6426 title: "Line Height",
6427 description: "Line height for terminal text.",
6428 field: Box::new(
6429 SettingField {
6430 json_path: Some("terminal.line_height"),
6431 pick: |settings_content| {
6432 settings_content.terminal.as_ref()?.line_height.as_ref()
6433 },
6434 write: |settings_content, value| {
6435 settings_content
6436 .terminal
6437 .get_or_insert_default()
6438 .line_height = value;
6439 },
6440 }
6441 .unimplemented(),
6442 ),
6443 metadata: None,
6444 files: USER,
6445 }),
6446 SettingsPageItem::SettingItem(SettingItem {
6447 title: "Cursor Shape",
6448 description: "Default cursor shape for the terminal (bar, block, underline, or hollow).",
6449 field: Box::new(SettingField {
6450 json_path: Some("terminal.cursor_shape"),
6451 pick: |settings_content| {
6452 settings_content.terminal.as_ref()?.cursor_shape.as_ref()
6453 },
6454 write: |settings_content, value| {
6455 settings_content
6456 .terminal
6457 .get_or_insert_default()
6458 .cursor_shape = value;
6459 },
6460 }),
6461 metadata: None,
6462 files: USER,
6463 }),
6464 SettingsPageItem::SettingItem(SettingItem {
6465 title: "Cursor Blinking",
6466 description: "Sets the cursor blinking behavior in the terminal.",
6467 field: Box::new(SettingField {
6468 json_path: Some("terminal.blinking"),
6469 pick: |settings_content| settings_content.terminal.as_ref()?.blinking.as_ref(),
6470 write: |settings_content, value| {
6471 settings_content.terminal.get_or_insert_default().blinking = value;
6472 },
6473 }),
6474 metadata: None,
6475 files: USER,
6476 }),
6477 SettingsPageItem::SettingItem(SettingItem {
6478 title: "Alternate Scroll",
6479 description: "Whether alternate scroll mode is active by default (converts mouse scroll to arrow keys in apps like Vim).",
6480 field: Box::new(SettingField {
6481 json_path: Some("terminal.alternate_scroll"),
6482 pick: |settings_content| {
6483 settings_content
6484 .terminal
6485 .as_ref()?
6486 .alternate_scroll
6487 .as_ref()
6488 },
6489 write: |settings_content, value| {
6490 settings_content
6491 .terminal
6492 .get_or_insert_default()
6493 .alternate_scroll = value;
6494 },
6495 }),
6496 metadata: None,
6497 files: USER,
6498 }),
6499 SettingsPageItem::SettingItem(SettingItem {
6500 title: "Minimum Contrast",
6501 description: "The minimum APCA perceptual contrast between foreground and background colors (0-106).",
6502 field: Box::new(SettingField {
6503 json_path: Some("terminal.minimum_contrast"),
6504 pick: |settings_content| {
6505 settings_content
6506 .terminal
6507 .as_ref()?
6508 .minimum_contrast
6509 .as_ref()
6510 },
6511 write: |settings_content, value| {
6512 settings_content
6513 .terminal
6514 .get_or_insert_default()
6515 .minimum_contrast = value;
6516 },
6517 }),
6518 metadata: None,
6519 files: USER,
6520 }),
6521 ]
6522 }
6523
6524 fn behavior_settings_section() -> [SettingsPageItem; 4] {
6525 [
6526 SettingsPageItem::SectionHeader("Behavior Settings"),
6527 SettingsPageItem::SettingItem(SettingItem {
6528 title: "Option As Meta",
6529 description: "Whether the option key behaves as the meta key.",
6530 field: Box::new(SettingField {
6531 json_path: Some("terminal.option_as_meta"),
6532 pick: |settings_content| {
6533 settings_content.terminal.as_ref()?.option_as_meta.as_ref()
6534 },
6535 write: |settings_content, value| {
6536 settings_content
6537 .terminal
6538 .get_or_insert_default()
6539 .option_as_meta = value;
6540 },
6541 }),
6542 metadata: None,
6543 files: USER,
6544 }),
6545 SettingsPageItem::SettingItem(SettingItem {
6546 title: "Copy On Select",
6547 description: "Whether selecting text in the terminal automatically copies to the system clipboard.",
6548 field: Box::new(SettingField {
6549 json_path: Some("terminal.copy_on_select"),
6550 pick: |settings_content| {
6551 settings_content.terminal.as_ref()?.copy_on_select.as_ref()
6552 },
6553 write: |settings_content, value| {
6554 settings_content
6555 .terminal
6556 .get_or_insert_default()
6557 .copy_on_select = value;
6558 },
6559 }),
6560 metadata: None,
6561 files: USER,
6562 }),
6563 SettingsPageItem::SettingItem(SettingItem {
6564 title: "Keep Selection On Copy",
6565 description: "Whether to keep the text selection after copying it to the clipboard.",
6566 field: Box::new(SettingField {
6567 json_path: Some("terminal.keep_selection_on_copy"),
6568 pick: |settings_content| {
6569 settings_content
6570 .terminal
6571 .as_ref()?
6572 .keep_selection_on_copy
6573 .as_ref()
6574 },
6575 write: |settings_content, value| {
6576 settings_content
6577 .terminal
6578 .get_or_insert_default()
6579 .keep_selection_on_copy = value;
6580 },
6581 }),
6582 metadata: None,
6583 files: USER,
6584 }),
6585 ]
6586 }
6587
6588 fn layout_settings_section() -> [SettingsPageItem; 3] {
6589 [
6590 SettingsPageItem::SectionHeader("Layout Settings"),
6591 SettingsPageItem::SettingItem(SettingItem {
6592 title: "Default Width",
6593 description: "Default width when the terminal is docked to the left or right (in pixels).",
6594 field: Box::new(SettingField {
6595 json_path: Some("terminal.default_width"),
6596 pick: |settings_content| {
6597 settings_content.terminal.as_ref()?.default_width.as_ref()
6598 },
6599 write: |settings_content, value| {
6600 settings_content
6601 .terminal
6602 .get_or_insert_default()
6603 .default_width = value;
6604 },
6605 }),
6606 metadata: None,
6607 files: USER,
6608 }),
6609 SettingsPageItem::SettingItem(SettingItem {
6610 title: "Default Height",
6611 description: "Default height when the terminal is docked to the bottom (in pixels).",
6612 field: Box::new(SettingField {
6613 json_path: Some("terminal.default_height"),
6614 pick: |settings_content| {
6615 settings_content.terminal.as_ref()?.default_height.as_ref()
6616 },
6617 write: |settings_content, value| {
6618 settings_content
6619 .terminal
6620 .get_or_insert_default()
6621 .default_height = value;
6622 },
6623 }),
6624 metadata: None,
6625 files: USER,
6626 }),
6627 ]
6628 }
6629
6630 fn advanced_settings_section() -> [SettingsPageItem; 3] {
6631 [
6632 SettingsPageItem::SectionHeader("Advanced Settings"),
6633 SettingsPageItem::SettingItem(SettingItem {
6634 title: "Max Scroll History Lines",
6635 description: "Maximum number of lines to keep in scrollback history (max: 100,000; 0 disables scrolling).",
6636 field: Box::new(SettingField {
6637 json_path: Some("terminal.max_scroll_history_lines"),
6638 pick: |settings_content| {
6639 settings_content
6640 .terminal
6641 .as_ref()?
6642 .max_scroll_history_lines
6643 .as_ref()
6644 },
6645 write: |settings_content, value| {
6646 settings_content
6647 .terminal
6648 .get_or_insert_default()
6649 .max_scroll_history_lines = value;
6650 },
6651 }),
6652 metadata: None,
6653 files: USER,
6654 }),
6655 SettingsPageItem::SettingItem(SettingItem {
6656 title: "Scroll Multiplier",
6657 description: "The multiplier for scrolling in the terminal with the mouse wheel",
6658 field: Box::new(SettingField {
6659 json_path: Some("terminal.scroll_multiplier"),
6660 pick: |settings_content| {
6661 settings_content
6662 .terminal
6663 .as_ref()?
6664 .scroll_multiplier
6665 .as_ref()
6666 },
6667 write: |settings_content, value| {
6668 settings_content
6669 .terminal
6670 .get_or_insert_default()
6671 .scroll_multiplier = value;
6672 },
6673 }),
6674 metadata: None,
6675 files: USER,
6676 }),
6677 ]
6678 }
6679
6680 fn toolbar_section() -> [SettingsPageItem; 2] {
6681 [
6682 SettingsPageItem::SectionHeader("Toolbar"),
6683 SettingsPageItem::SettingItem(SettingItem {
6684 title: "Breadcrumbs",
6685 description: "Display the terminal title in breadcrumbs inside the terminal pane.",
6686 field: Box::new(SettingField {
6687 json_path: Some("terminal.toolbar.breadcrumbs"),
6688 pick: |settings_content| {
6689 settings_content
6690 .terminal
6691 .as_ref()?
6692 .toolbar
6693 .as_ref()?
6694 .breadcrumbs
6695 .as_ref()
6696 },
6697 write: |settings_content, value| {
6698 settings_content
6699 .terminal
6700 .get_or_insert_default()
6701 .toolbar
6702 .get_or_insert_default()
6703 .breadcrumbs = value;
6704 },
6705 }),
6706 metadata: None,
6707 files: USER,
6708 }),
6709 ]
6710 }
6711
6712 fn scrollbar_section() -> [SettingsPageItem; 2] {
6713 [
6714 SettingsPageItem::SectionHeader("Scrollbar"),
6715 SettingsPageItem::SettingItem(SettingItem {
6716 title: "Show Scrollbar",
6717 description: "When to show the scrollbar in the terminal.",
6718 field: Box::new(SettingField {
6719 json_path: Some("terminal.scrollbar.show"),
6720 pick: |settings_content| {
6721 show_scrollbar_or_editor(settings_content, |settings_content| {
6722 settings_content
6723 .terminal
6724 .as_ref()?
6725 .scrollbar
6726 .as_ref()?
6727 .show
6728 .as_ref()
6729 })
6730 },
6731 write: |settings_content, value| {
6732 settings_content
6733 .terminal
6734 .get_or_insert_default()
6735 .scrollbar
6736 .get_or_insert_default()
6737 .show = value;
6738 },
6739 }),
6740 metadata: None,
6741 files: USER,
6742 }),
6743 ]
6744 }
6745
6746 SettingsPage {
6747 title: "Terminal",
6748 items: concat_sections![
6749 environment_section(),
6750 font_section(),
6751 display_settings_section(),
6752 behavior_settings_section(),
6753 layout_settings_section(),
6754 advanced_settings_section(),
6755 toolbar_section(),
6756 scrollbar_section(),
6757 ],
6758 }
6759}
6760
6761fn version_control_page() -> SettingsPage {
6762 fn git_integration_section() -> [SettingsPageItem; 2] {
6763 [
6764 SettingsPageItem::SectionHeader("Git Integration"),
6765 SettingsPageItem::DynamicItem(DynamicItem {
6766 discriminant: SettingItem {
6767 files: USER,
6768 title: "Disable Git Integration",
6769 description: "Disable all Git integration features in Zed.",
6770 field: Box::new(SettingField::<bool> {
6771 json_path: Some("git.disable_git"),
6772 pick: |settings_content| {
6773 settings_content
6774 .git
6775 .as_ref()?
6776 .enabled
6777 .as_ref()?
6778 .disable_git
6779 .as_ref()
6780 },
6781 write: |settings_content, value| {
6782 settings_content
6783 .git
6784 .get_or_insert_default()
6785 .enabled
6786 .get_or_insert_default()
6787 .disable_git = value;
6788 },
6789 }),
6790 metadata: None,
6791 },
6792 pick_discriminant: |settings_content| {
6793 let disabled = settings_content
6794 .git
6795 .as_ref()?
6796 .enabled
6797 .as_ref()?
6798 .disable_git
6799 .unwrap_or(false);
6800 Some(if disabled { 0 } else { 1 })
6801 },
6802 fields: vec![
6803 vec![],
6804 vec![
6805 SettingItem {
6806 files: USER,
6807 title: "Enable Git Status",
6808 description: "Show Git status information in the editor.",
6809 field: Box::new(SettingField::<bool> {
6810 json_path: Some("git.enable_status"),
6811 pick: |settings_content| {
6812 settings_content
6813 .git
6814 .as_ref()?
6815 .enabled
6816 .as_ref()?
6817 .enable_status
6818 .as_ref()
6819 },
6820 write: |settings_content, value| {
6821 settings_content
6822 .git
6823 .get_or_insert_default()
6824 .enabled
6825 .get_or_insert_default()
6826 .enable_status = value;
6827 },
6828 }),
6829 metadata: None,
6830 },
6831 SettingItem {
6832 files: USER,
6833 title: "Enable Git Diff",
6834 description: "Show Git diff information in the editor.",
6835 field: Box::new(SettingField::<bool> {
6836 json_path: Some("git.enable_diff"),
6837 pick: |settings_content| {
6838 settings_content
6839 .git
6840 .as_ref()?
6841 .enabled
6842 .as_ref()?
6843 .enable_diff
6844 .as_ref()
6845 },
6846 write: |settings_content, value| {
6847 settings_content
6848 .git
6849 .get_or_insert_default()
6850 .enabled
6851 .get_or_insert_default()
6852 .enable_diff = value;
6853 },
6854 }),
6855 metadata: None,
6856 },
6857 ],
6858 ],
6859 }),
6860 ]
6861 }
6862
6863 fn git_gutter_section() -> [SettingsPageItem; 3] {
6864 [
6865 SettingsPageItem::SectionHeader("Git Gutter"),
6866 SettingsPageItem::SettingItem(SettingItem {
6867 title: "Visibility",
6868 description: "Control whether Git status is shown in the editor's gutter.",
6869 field: Box::new(SettingField {
6870 json_path: Some("git.git_gutter"),
6871 pick: |settings_content| settings_content.git.as_ref()?.git_gutter.as_ref(),
6872 write: |settings_content, value| {
6873 settings_content.git.get_or_insert_default().git_gutter = value;
6874 },
6875 }),
6876 metadata: None,
6877 files: USER,
6878 }),
6879 // todo(settings_ui): Figure out the right default for this value in default.json
6880 SettingsPageItem::SettingItem(SettingItem {
6881 title: "Debounce",
6882 description: "Debounce threshold in milliseconds after which changes are reflected in the Git gutter.",
6883 field: Box::new(SettingField {
6884 json_path: Some("git.gutter_debounce"),
6885 pick: |settings_content| {
6886 settings_content.git.as_ref()?.gutter_debounce.as_ref()
6887 },
6888 write: |settings_content, value| {
6889 settings_content.git.get_or_insert_default().gutter_debounce = value;
6890 },
6891 }),
6892 metadata: None,
6893 files: USER,
6894 }),
6895 ]
6896 }
6897
6898 fn inline_git_blame_section() -> [SettingsPageItem; 6] {
6899 [
6900 SettingsPageItem::SectionHeader("Inline Git Blame"),
6901 SettingsPageItem::SettingItem(SettingItem {
6902 title: "Enabled",
6903 description: "Whether or not to show Git blame data inline in the currently focused line.",
6904 field: Box::new(SettingField {
6905 json_path: Some("git.inline_blame.enabled"),
6906 pick: |settings_content| {
6907 settings_content
6908 .git
6909 .as_ref()?
6910 .inline_blame
6911 .as_ref()?
6912 .enabled
6913 .as_ref()
6914 },
6915 write: |settings_content, value| {
6916 settings_content
6917 .git
6918 .get_or_insert_default()
6919 .inline_blame
6920 .get_or_insert_default()
6921 .enabled = value;
6922 },
6923 }),
6924 metadata: None,
6925 files: USER,
6926 }),
6927 SettingsPageItem::SettingItem(SettingItem {
6928 title: "Delay",
6929 description: "The delay after which the inline blame information is shown.",
6930 field: Box::new(SettingField {
6931 json_path: Some("git.inline_blame.delay_ms"),
6932 pick: |settings_content| {
6933 settings_content
6934 .git
6935 .as_ref()?
6936 .inline_blame
6937 .as_ref()?
6938 .delay_ms
6939 .as_ref()
6940 },
6941 write: |settings_content, value| {
6942 settings_content
6943 .git
6944 .get_or_insert_default()
6945 .inline_blame
6946 .get_or_insert_default()
6947 .delay_ms = value;
6948 },
6949 }),
6950 metadata: None,
6951 files: USER,
6952 }),
6953 SettingsPageItem::SettingItem(SettingItem {
6954 title: "Padding",
6955 description: "Padding between the end of the source line and the start of the inline blame in columns.",
6956 field: Box::new(SettingField {
6957 json_path: Some("git.inline_blame.padding"),
6958 pick: |settings_content| {
6959 settings_content
6960 .git
6961 .as_ref()?
6962 .inline_blame
6963 .as_ref()?
6964 .padding
6965 .as_ref()
6966 },
6967 write: |settings_content, value| {
6968 settings_content
6969 .git
6970 .get_or_insert_default()
6971 .inline_blame
6972 .get_or_insert_default()
6973 .padding = value;
6974 },
6975 }),
6976 metadata: None,
6977 files: USER,
6978 }),
6979 SettingsPageItem::SettingItem(SettingItem {
6980 title: "Minimum Column",
6981 description: "The minimum column number at which to show the inline blame information.",
6982 field: Box::new(SettingField {
6983 json_path: Some("git.inline_blame.min_column"),
6984 pick: |settings_content| {
6985 settings_content
6986 .git
6987 .as_ref()?
6988 .inline_blame
6989 .as_ref()?
6990 .min_column
6991 .as_ref()
6992 },
6993 write: |settings_content, value| {
6994 settings_content
6995 .git
6996 .get_or_insert_default()
6997 .inline_blame
6998 .get_or_insert_default()
6999 .min_column = value;
7000 },
7001 }),
7002 metadata: None,
7003 files: USER,
7004 }),
7005 SettingsPageItem::SettingItem(SettingItem {
7006 title: "Show Commit Summary",
7007 description: "Show commit summary as part of the inline blame.",
7008 field: Box::new(SettingField {
7009 json_path: Some("git.inline_blame.show_commit_summary"),
7010 pick: |settings_content| {
7011 settings_content
7012 .git
7013 .as_ref()?
7014 .inline_blame
7015 .as_ref()?
7016 .show_commit_summary
7017 .as_ref()
7018 },
7019 write: |settings_content, value| {
7020 settings_content
7021 .git
7022 .get_or_insert_default()
7023 .inline_blame
7024 .get_or_insert_default()
7025 .show_commit_summary = value;
7026 },
7027 }),
7028 metadata: None,
7029 files: USER,
7030 }),
7031 ]
7032 }
7033
7034 fn git_blame_view_section() -> [SettingsPageItem; 2] {
7035 [
7036 SettingsPageItem::SectionHeader("Git Blame View"),
7037 SettingsPageItem::SettingItem(SettingItem {
7038 title: "Show Avatar",
7039 description: "Show the avatar of the author of the commit.",
7040 field: Box::new(SettingField {
7041 json_path: Some("git.blame.show_avatar"),
7042 pick: |settings_content| {
7043 settings_content
7044 .git
7045 .as_ref()?
7046 .blame
7047 .as_ref()?
7048 .show_avatar
7049 .as_ref()
7050 },
7051 write: |settings_content, value| {
7052 settings_content
7053 .git
7054 .get_or_insert_default()
7055 .blame
7056 .get_or_insert_default()
7057 .show_avatar = value;
7058 },
7059 }),
7060 metadata: None,
7061 files: USER,
7062 }),
7063 ]
7064 }
7065
7066 fn branch_picker_section() -> [SettingsPageItem; 2] {
7067 [
7068 SettingsPageItem::SectionHeader("Branch Picker"),
7069 SettingsPageItem::SettingItem(SettingItem {
7070 title: "Show Author Name",
7071 description: "Show author name as part of the commit information in branch picker.",
7072 field: Box::new(SettingField {
7073 json_path: Some("git.branch_picker.show_author_name"),
7074 pick: |settings_content| {
7075 settings_content
7076 .git
7077 .as_ref()?
7078 .branch_picker
7079 .as_ref()?
7080 .show_author_name
7081 .as_ref()
7082 },
7083 write: |settings_content, value| {
7084 settings_content
7085 .git
7086 .get_or_insert_default()
7087 .branch_picker
7088 .get_or_insert_default()
7089 .show_author_name = value;
7090 },
7091 }),
7092 metadata: None,
7093 files: USER,
7094 }),
7095 ]
7096 }
7097
7098 fn git_hunks_section() -> [SettingsPageItem; 3] {
7099 [
7100 SettingsPageItem::SectionHeader("Git Hunks"),
7101 SettingsPageItem::SettingItem(SettingItem {
7102 title: "Hunk Style",
7103 description: "How Git hunks are displayed visually in the editor.",
7104 field: Box::new(SettingField {
7105 json_path: Some("git.hunk_style"),
7106 pick: |settings_content| settings_content.git.as_ref()?.hunk_style.as_ref(),
7107 write: |settings_content, value| {
7108 settings_content.git.get_or_insert_default().hunk_style = value;
7109 },
7110 }),
7111 metadata: None,
7112 files: USER,
7113 }),
7114 SettingsPageItem::SettingItem(SettingItem {
7115 title: "Path Style",
7116 description: "Should the name or path be displayed first in the git view.",
7117 field: Box::new(SettingField {
7118 json_path: Some("git.path_style"),
7119 pick: |settings_content| settings_content.git.as_ref()?.path_style.as_ref(),
7120 write: |settings_content, value| {
7121 settings_content.git.get_or_insert_default().path_style = value;
7122 },
7123 }),
7124 metadata: None,
7125 files: USER,
7126 }),
7127 ]
7128 }
7129
7130 SettingsPage {
7131 title: "Version Control",
7132 items: concat_sections![
7133 git_integration_section(),
7134 git_gutter_section(),
7135 inline_git_blame_section(),
7136 git_blame_view_section(),
7137 branch_picker_section(),
7138 git_hunks_section(),
7139 ],
7140 }
7141}
7142
7143fn collaboration_page() -> SettingsPage {
7144 fn calls_section() -> [SettingsPageItem; 3] {
7145 [
7146 SettingsPageItem::SectionHeader("Calls"),
7147 SettingsPageItem::SettingItem(SettingItem {
7148 title: "Mute On Join",
7149 description: "Whether the microphone should be muted when joining a channel or a call.",
7150 field: Box::new(SettingField {
7151 json_path: Some("calls.mute_on_join"),
7152 pick: |settings_content| settings_content.calls.as_ref()?.mute_on_join.as_ref(),
7153 write: |settings_content, value| {
7154 settings_content.calls.get_or_insert_default().mute_on_join = value;
7155 },
7156 }),
7157 metadata: None,
7158 files: USER,
7159 }),
7160 SettingsPageItem::SettingItem(SettingItem {
7161 title: "Share On Join",
7162 description: "Whether your current project should be shared when joining an empty channel.",
7163 field: Box::new(SettingField {
7164 json_path: Some("calls.share_on_join"),
7165 pick: |settings_content| {
7166 settings_content.calls.as_ref()?.share_on_join.as_ref()
7167 },
7168 write: |settings_content, value| {
7169 settings_content.calls.get_or_insert_default().share_on_join = value;
7170 },
7171 }),
7172 metadata: None,
7173 files: USER,
7174 }),
7175 ]
7176 }
7177
7178 fn audio_settings() -> [SettingsPageItem; 3] {
7179 [
7180 SettingsPageItem::ActionLink(ActionLink {
7181 title: "Test Audio".into(),
7182 description: Some("Test your microphone and speaker setup".into()),
7183 button_text: "Test Audio".into(),
7184 on_click: Arc::new(|_settings_window, window, cx| {
7185 open_audio_test_window(window, cx);
7186 }),
7187 files: USER,
7188 }),
7189 SettingsPageItem::SettingItem(SettingItem {
7190 title: "Output Audio Device",
7191 description: "Select output audio device",
7192 field: Box::new(SettingField {
7193 json_path: Some("audio.experimental.output_audio_device"),
7194 pick: |settings_content| {
7195 settings_content
7196 .audio
7197 .as_ref()?
7198 .output_audio_device
7199 .as_ref()
7200 .or(DEFAULT_EMPTY_AUDIO_OUTPUT)
7201 },
7202 write: |settings_content, value| {
7203 settings_content
7204 .audio
7205 .get_or_insert_default()
7206 .output_audio_device = value;
7207 },
7208 }),
7209 metadata: None,
7210 files: USER,
7211 }),
7212 SettingsPageItem::SettingItem(SettingItem {
7213 title: "Input Audio Device",
7214 description: "Select input audio device",
7215 field: Box::new(SettingField {
7216 json_path: Some("audio.experimental.input_audio_device"),
7217 pick: |settings_content| {
7218 settings_content
7219 .audio
7220 .as_ref()?
7221 .input_audio_device
7222 .as_ref()
7223 .or(DEFAULT_EMPTY_AUDIO_INPUT)
7224 },
7225 write: |settings_content, value| {
7226 settings_content
7227 .audio
7228 .get_or_insert_default()
7229 .input_audio_device = value;
7230 },
7231 }),
7232 metadata: None,
7233 files: USER,
7234 }),
7235 ]
7236 }
7237
7238 SettingsPage {
7239 title: "Collaboration",
7240 items: concat_sections![calls_section(), audio_settings()],
7241 }
7242}
7243
7244fn ai_page(cx: &App) -> SettingsPage {
7245 fn general_section() -> [SettingsPageItem; 2] {
7246 [
7247 SettingsPageItem::SectionHeader("General"),
7248 SettingsPageItem::SettingItem(SettingItem {
7249 title: "Disable AI",
7250 description: "Whether to disable all AI features in Zed.",
7251 field: Box::new(SettingField {
7252 json_path: Some("disable_ai"),
7253 pick: |settings_content| settings_content.project.disable_ai.as_ref(),
7254 write: |settings_content, value| {
7255 settings_content.project.disable_ai = value;
7256 },
7257 }),
7258 metadata: None,
7259 files: USER | PROJECT,
7260 }),
7261 ]
7262 }
7263
7264 fn agent_configuration_section(cx: &App) -> Box<[SettingsPageItem]> {
7265 let mut items = vec![
7266 SettingsPageItem::SectionHeader("Agent Configuration"),
7267 SettingsPageItem::SubPageLink(SubPageLink {
7268 title: "Tool Permissions".into(),
7269 r#type: Default::default(),
7270 json_path: Some("agent.tool_permissions"),
7271 description: Some("Set up regex patterns to auto-allow, auto-deny, or always request confirmation, for specific tool inputs.".into()),
7272 in_json: true,
7273 files: USER,
7274 render: render_tool_permissions_setup_page,
7275 }),
7276 ];
7277
7278 if cx.has_flag::<AgentV2FeatureFlag>() {
7279 items.push(SettingsPageItem::SettingItem(SettingItem {
7280 title: "New Thread Location",
7281 description: "Whether to start a new thread in the current local project or in a new Git worktree.",
7282 field: Box::new(SettingField {
7283 json_path: Some("agent.new_thread_location"),
7284 pick: |settings_content| {
7285 settings_content
7286 .agent
7287 .as_ref()?
7288 .new_thread_location
7289 .as_ref()
7290 },
7291 write: |settings_content, value| {
7292 settings_content
7293 .agent
7294 .get_or_insert_default()
7295 .new_thread_location = value;
7296 },
7297 }),
7298 metadata: None,
7299 files: USER,
7300 }));
7301 }
7302
7303 items.extend([
7304 SettingsPageItem::SettingItem(SettingItem {
7305 title: "Single File Review",
7306 description: "When enabled, agent edits will also be displayed in single-file buffers for review.",
7307 field: Box::new(SettingField {
7308 json_path: Some("agent.single_file_review"),
7309 pick: |settings_content| {
7310 settings_content.agent.as_ref()?.single_file_review.as_ref()
7311 },
7312 write: |settings_content, value| {
7313 settings_content
7314 .agent
7315 .get_or_insert_default()
7316 .single_file_review = value;
7317 },
7318 }),
7319 metadata: None,
7320 files: USER,
7321 }),
7322 SettingsPageItem::SettingItem(SettingItem {
7323 title: "Enable Feedback",
7324 description: "Show voting thumbs up/down icon buttons for feedback on agent edits.",
7325 field: Box::new(SettingField {
7326 json_path: Some("agent.enable_feedback"),
7327 pick: |settings_content| {
7328 settings_content.agent.as_ref()?.enable_feedback.as_ref()
7329 },
7330 write: |settings_content, value| {
7331 settings_content
7332 .agent
7333 .get_or_insert_default()
7334 .enable_feedback = value;
7335 },
7336 }),
7337 metadata: None,
7338 files: USER,
7339 }),
7340 SettingsPageItem::SettingItem(SettingItem {
7341 title: "Notify When Agent Waiting",
7342 description: "Where to show notifications when the agent has completed its response or needs confirmation before running a tool action.",
7343 field: Box::new(SettingField {
7344 json_path: Some("agent.notify_when_agent_waiting"),
7345 pick: |settings_content| {
7346 settings_content
7347 .agent
7348 .as_ref()?
7349 .notify_when_agent_waiting
7350 .as_ref()
7351 },
7352 write: |settings_content, value| {
7353 settings_content
7354 .agent
7355 .get_or_insert_default()
7356 .notify_when_agent_waiting = value;
7357 },
7358 }),
7359 metadata: None,
7360 files: USER,
7361 }),
7362 SettingsPageItem::SettingItem(SettingItem {
7363 title: "Play Sound When Agent Done",
7364 description: "When to play a sound when the agent has either completed its response, or needs user input.",
7365 field: Box::new(SettingField {
7366 json_path: Some("agent.play_sound_when_agent_done"),
7367 pick: |settings_content| {
7368 settings_content
7369 .agent
7370 .as_ref()?
7371 .play_sound_when_agent_done
7372 .as_ref()
7373 },
7374 write: |settings_content, value| {
7375 settings_content
7376 .agent
7377 .get_or_insert_default()
7378 .play_sound_when_agent_done = value;
7379 },
7380 }),
7381 metadata: None,
7382 files: USER,
7383 }),
7384 SettingsPageItem::SettingItem(SettingItem {
7385 title: "Expand Edit Card",
7386 description: "Whether to have edit cards in the agent panel expanded, showing a Preview of the diff.",
7387 field: Box::new(SettingField {
7388 json_path: Some("agent.expand_edit_card"),
7389 pick: |settings_content| {
7390 settings_content.agent.as_ref()?.expand_edit_card.as_ref()
7391 },
7392 write: |settings_content, value| {
7393 settings_content
7394 .agent
7395 .get_or_insert_default()
7396 .expand_edit_card = value;
7397 },
7398 }),
7399 metadata: None,
7400 files: USER,
7401 }),
7402 SettingsPageItem::SettingItem(SettingItem {
7403 title: "Expand Terminal Card",
7404 description: "Whether to have terminal cards in the agent panel expanded, showing the whole command output.",
7405 field: Box::new(SettingField {
7406 json_path: Some("agent.expand_terminal_card"),
7407 pick: |settings_content| {
7408 settings_content
7409 .agent
7410 .as_ref()?
7411 .expand_terminal_card
7412 .as_ref()
7413 },
7414 write: |settings_content, value| {
7415 settings_content
7416 .agent
7417 .get_or_insert_default()
7418 .expand_terminal_card = value;
7419 },
7420 }),
7421 metadata: None,
7422 files: USER,
7423 }),
7424 SettingsPageItem::SettingItem(SettingItem {
7425 title: "Thinking Display",
7426 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.",
7427 field: Box::new(SettingField {
7428 json_path: Some("agent.thinking_display"),
7429 pick: |settings_content| {
7430 settings_content
7431 .agent
7432 .as_ref()?
7433 .thinking_display
7434 .as_ref()
7435 },
7436 write: |settings_content, value| {
7437 settings_content
7438 .agent
7439 .get_or_insert_default()
7440 .thinking_display = value;
7441 },
7442 }),
7443 metadata: None,
7444 files: USER,
7445 }),
7446 SettingsPageItem::SettingItem(SettingItem {
7447 title: "Cancel Generation On Terminal Stop",
7448 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.",
7449 field: Box::new(SettingField {
7450 json_path: Some("agent.cancel_generation_on_terminal_stop"),
7451 pick: |settings_content| {
7452 settings_content
7453 .agent
7454 .as_ref()?
7455 .cancel_generation_on_terminal_stop
7456 .as_ref()
7457 },
7458 write: |settings_content, value| {
7459 settings_content
7460 .agent
7461 .get_or_insert_default()
7462 .cancel_generation_on_terminal_stop = value;
7463 },
7464 }),
7465 metadata: None,
7466 files: USER,
7467 }),
7468 SettingsPageItem::SettingItem(SettingItem {
7469 title: "Use Modifier To Send",
7470 description: "Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages.",
7471 field: Box::new(SettingField {
7472 json_path: Some("agent.use_modifier_to_send"),
7473 pick: |settings_content| {
7474 settings_content
7475 .agent
7476 .as_ref()?
7477 .use_modifier_to_send
7478 .as_ref()
7479 },
7480 write: |settings_content, value| {
7481 settings_content
7482 .agent
7483 .get_or_insert_default()
7484 .use_modifier_to_send = value;
7485 },
7486 }),
7487 metadata: None,
7488 files: USER,
7489 }),
7490 SettingsPageItem::SettingItem(SettingItem {
7491 title: "Message Editor Min Lines",
7492 description: "Minimum number of lines to display in the agent message editor.",
7493 field: Box::new(SettingField {
7494 json_path: Some("agent.message_editor_min_lines"),
7495 pick: |settings_content| {
7496 settings_content
7497 .agent
7498 .as_ref()?
7499 .message_editor_min_lines
7500 .as_ref()
7501 },
7502 write: |settings_content, value| {
7503 settings_content
7504 .agent
7505 .get_or_insert_default()
7506 .message_editor_min_lines = value;
7507 },
7508 }),
7509 metadata: None,
7510 files: USER,
7511 }),
7512 SettingsPageItem::SettingItem(SettingItem {
7513 title: "Show Turn Stats",
7514 description: "Whether to show turn statistics like elapsed time during generation and final turn duration.",
7515 field: Box::new(SettingField {
7516 json_path: Some("agent.show_turn_stats"),
7517 pick: |settings_content| {
7518 settings_content.agent.as_ref()?.show_turn_stats.as_ref()
7519 },
7520 write: |settings_content, value| {
7521 settings_content
7522 .agent
7523 .get_or_insert_default()
7524 .show_turn_stats = value;
7525 },
7526 }),
7527 metadata: None,
7528 files: USER,
7529 }),
7530 SettingsPageItem::SettingItem(SettingItem {
7531 title: "Show Merge Conflict Indicator",
7532 description: "Whether to show the merge conflict indicator in the status bar that offers to resolve conflicts using the agent.",
7533 field: Box::new(SettingField {
7534 json_path: Some("agent.show_merge_conflict_indicator"),
7535 pick: |settings_content| {
7536 settings_content.agent.as_ref()?.show_merge_conflict_indicator.as_ref()
7537 },
7538 write: |settings_content, value| {
7539 settings_content
7540 .agent
7541 .get_or_insert_default()
7542 .show_merge_conflict_indicator = value;
7543 },
7544 }),
7545 metadata: None,
7546 files: USER,
7547 }),
7548 ]);
7549
7550 items.into_boxed_slice()
7551 }
7552
7553 fn context_servers_section() -> [SettingsPageItem; 2] {
7554 [
7555 SettingsPageItem::SectionHeader("Context Servers"),
7556 SettingsPageItem::SettingItem(SettingItem {
7557 title: "Context Server Timeout",
7558 description: "Default timeout in seconds for context server tool calls. Can be overridden per-server in context_servers configuration.",
7559 field: Box::new(SettingField {
7560 json_path: Some("context_server_timeout"),
7561 pick: |settings_content| {
7562 settings_content.project.context_server_timeout.as_ref()
7563 },
7564 write: |settings_content, value| {
7565 settings_content.project.context_server_timeout = value;
7566 },
7567 }),
7568 metadata: None,
7569 files: USER | PROJECT,
7570 }),
7571 ]
7572 }
7573
7574 fn edit_prediction_display_sub_section() -> [SettingsPageItem; 1] {
7575 [SettingsPageItem::SettingItem(SettingItem {
7576 title: "Display Mode",
7577 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.",
7578 field: Box::new(SettingField {
7579 json_path: Some("edit_prediction.display_mode"),
7580 pick: |settings_content| {
7581 settings_content
7582 .project
7583 .all_languages
7584 .edit_predictions
7585 .as_ref()?
7586 .mode
7587 .as_ref()
7588 },
7589 write: |settings_content, value| {
7590 settings_content
7591 .project
7592 .all_languages
7593 .edit_predictions
7594 .get_or_insert_default()
7595 .mode = value;
7596 },
7597 }),
7598 metadata: None,
7599 files: USER,
7600 })]
7601 }
7602
7603 SettingsPage {
7604 title: "AI",
7605 items: concat_sections![
7606 general_section(),
7607 agent_configuration_section(cx),
7608 context_servers_section(),
7609 edit_prediction_language_settings_section(),
7610 edit_prediction_display_sub_section()
7611 ],
7612 }
7613}
7614
7615fn network_page() -> SettingsPage {
7616 fn network_section() -> [SettingsPageItem; 3] {
7617 [
7618 SettingsPageItem::SectionHeader("Network"),
7619 SettingsPageItem::SettingItem(SettingItem {
7620 title: "Proxy",
7621 description: "The proxy to use for network requests.",
7622 field: Box::new(SettingField {
7623 json_path: Some("proxy"),
7624 pick: |settings_content| settings_content.proxy.as_ref(),
7625 write: |settings_content, value| {
7626 settings_content.proxy = value;
7627 },
7628 }),
7629 metadata: Some(Box::new(SettingsFieldMetadata {
7630 placeholder: Some("socks5h://localhost:10808"),
7631 ..Default::default()
7632 })),
7633 files: USER,
7634 }),
7635 SettingsPageItem::SettingItem(SettingItem {
7636 title: "Server URL",
7637 description: "The URL of the Zed server to connect to.",
7638 field: Box::new(SettingField {
7639 json_path: Some("server_url"),
7640 pick: |settings_content| settings_content.server_url.as_ref(),
7641 write: |settings_content, value| {
7642 settings_content.server_url = value;
7643 },
7644 }),
7645 metadata: Some(Box::new(SettingsFieldMetadata {
7646 placeholder: Some("https://zed.dev"),
7647 ..Default::default()
7648 })),
7649 files: USER,
7650 }),
7651 ]
7652 }
7653
7654 SettingsPage {
7655 title: "Network",
7656 items: concat_sections![network_section()],
7657 }
7658}
7659
7660fn language_settings_field<T>(
7661 settings_content: &SettingsContent,
7662 get_language_setting_field: fn(&LanguageSettingsContent) -> Option<&T>,
7663) -> Option<&T> {
7664 let all_languages = &settings_content.project.all_languages;
7665
7666 active_language()
7667 .and_then(|current_language_name| {
7668 all_languages
7669 .languages
7670 .0
7671 .get(current_language_name.as_ref())
7672 })
7673 .and_then(get_language_setting_field)
7674 .or_else(|| get_language_setting_field(&all_languages.defaults))
7675}
7676
7677fn language_settings_field_mut<T>(
7678 settings_content: &mut SettingsContent,
7679 value: Option<T>,
7680 write: fn(&mut LanguageSettingsContent, Option<T>),
7681) {
7682 let all_languages = &mut settings_content.project.all_languages;
7683 let language_content = if let Some(current_language) = active_language() {
7684 all_languages
7685 .languages
7686 .0
7687 .entry(current_language.to_string())
7688 .or_default()
7689 } else {
7690 &mut all_languages.defaults
7691 };
7692 write(language_content, value);
7693}
7694
7695fn language_settings_data() -> Box<[SettingsPageItem]> {
7696 fn indentation_section() -> [SettingsPageItem; 5] {
7697 [
7698 SettingsPageItem::SectionHeader("Indentation"),
7699 SettingsPageItem::SettingItem(SettingItem {
7700 title: "Tab Size",
7701 description: "How many columns a tab should occupy.",
7702 field: Box::new(SettingField {
7703 json_path: Some("languages.$(language).tab_size"), // TODO(cameron): not JQ syntax because not URL-safe
7704 pick: |settings_content| {
7705 language_settings_field(settings_content, |language| {
7706 language.tab_size.as_ref()
7707 })
7708 },
7709 write: |settings_content, value| {
7710 language_settings_field_mut(settings_content, value, |language, value| {
7711 language.tab_size = value;
7712 })
7713 },
7714 }),
7715 metadata: None,
7716 files: USER | PROJECT,
7717 }),
7718 SettingsPageItem::SettingItem(SettingItem {
7719 title: "Hard Tabs",
7720 description: "Whether to indent lines using tab characters, as opposed to multiple spaces.",
7721 field: Box::new(SettingField {
7722 json_path: Some("languages.$(language).hard_tabs"),
7723 pick: |settings_content| {
7724 language_settings_field(settings_content, |language| {
7725 language.hard_tabs.as_ref()
7726 })
7727 },
7728 write: |settings_content, value| {
7729 language_settings_field_mut(settings_content, value, |language, value| {
7730 language.hard_tabs = value;
7731 })
7732 },
7733 }),
7734 metadata: None,
7735 files: USER | PROJECT,
7736 }),
7737 SettingsPageItem::SettingItem(SettingItem {
7738 title: "Auto Indent",
7739 description: "Controls automatic indentation behavior when typing.",
7740 field: Box::new(SettingField {
7741 json_path: Some("languages.$(language).auto_indent"),
7742 pick: |settings_content| {
7743 language_settings_field(settings_content, |language| {
7744 language.auto_indent.as_ref()
7745 })
7746 },
7747 write: |settings_content, value| {
7748 language_settings_field_mut(settings_content, value, |language, value| {
7749 language.auto_indent = value;
7750 })
7751 },
7752 }),
7753 metadata: None,
7754 files: USER | PROJECT,
7755 }),
7756 SettingsPageItem::SettingItem(SettingItem {
7757 title: "Auto Indent On Paste",
7758 description: "Whether indentation of pasted content should be adjusted based on the context.",
7759 field: Box::new(SettingField {
7760 json_path: Some("languages.$(language).auto_indent_on_paste"),
7761 pick: |settings_content| {
7762 language_settings_field(settings_content, |language| {
7763 language.auto_indent_on_paste.as_ref()
7764 })
7765 },
7766 write: |settings_content, value| {
7767 language_settings_field_mut(settings_content, value, |language, value| {
7768 language.auto_indent_on_paste = value;
7769 })
7770 },
7771 }),
7772 metadata: None,
7773 files: USER | PROJECT,
7774 }),
7775 ]
7776 }
7777
7778 fn wrapping_section() -> [SettingsPageItem; 6] {
7779 [
7780 SettingsPageItem::SectionHeader("Wrapping"),
7781 SettingsPageItem::SettingItem(SettingItem {
7782 title: "Soft Wrap",
7783 description: "How to soft-wrap long lines of text.",
7784 field: Box::new(SettingField {
7785 json_path: Some("languages.$(language).soft_wrap"),
7786 pick: |settings_content| {
7787 language_settings_field(settings_content, |language| {
7788 language.soft_wrap.as_ref()
7789 })
7790 },
7791 write: |settings_content, value| {
7792 language_settings_field_mut(settings_content, value, |language, value| {
7793 language.soft_wrap = value;
7794 })
7795 },
7796 }),
7797 metadata: None,
7798 files: USER | PROJECT,
7799 }),
7800 SettingsPageItem::SettingItem(SettingItem {
7801 title: "Show Wrap Guides",
7802 description: "Show wrap guides in the editor.",
7803 field: Box::new(SettingField {
7804 json_path: Some("languages.$(language).show_wrap_guides"),
7805 pick: |settings_content| {
7806 language_settings_field(settings_content, |language| {
7807 language.show_wrap_guides.as_ref()
7808 })
7809 },
7810 write: |settings_content, value| {
7811 language_settings_field_mut(settings_content, value, |language, value| {
7812 language.show_wrap_guides = value;
7813 })
7814 },
7815 }),
7816 metadata: None,
7817 files: USER | PROJECT,
7818 }),
7819 SettingsPageItem::SettingItem(SettingItem {
7820 title: "Preferred Line Length",
7821 description: "The column at which to soft-wrap lines, for buffers where soft-wrap is enabled.",
7822 field: Box::new(SettingField {
7823 json_path: Some("languages.$(language).preferred_line_length"),
7824 pick: |settings_content| {
7825 language_settings_field(settings_content, |language| {
7826 language.preferred_line_length.as_ref()
7827 })
7828 },
7829 write: |settings_content, value| {
7830 language_settings_field_mut(settings_content, value, |language, value| {
7831 language.preferred_line_length = value;
7832 })
7833 },
7834 }),
7835 metadata: None,
7836 files: USER | PROJECT,
7837 }),
7838 SettingsPageItem::SettingItem(SettingItem {
7839 title: "Wrap Guides",
7840 description: "Character counts at which to show wrap guides in the editor.",
7841 field: Box::new(
7842 SettingField {
7843 json_path: Some("languages.$(language).wrap_guides"),
7844 pick: |settings_content| {
7845 language_settings_field(settings_content, |language| {
7846 language.wrap_guides.as_ref()
7847 })
7848 },
7849 write: |settings_content, value| {
7850 language_settings_field_mut(
7851 settings_content,
7852 value,
7853 |language, value| {
7854 language.wrap_guides = value;
7855 },
7856 )
7857 },
7858 }
7859 .unimplemented(),
7860 ),
7861 metadata: None,
7862 files: USER | PROJECT,
7863 }),
7864 SettingsPageItem::SettingItem(SettingItem {
7865 title: "Allow Rewrap",
7866 description: "Controls where the `editor::rewrap` action is allowed for this language.",
7867 field: Box::new(SettingField {
7868 json_path: Some("languages.$(language).allow_rewrap"),
7869 pick: |settings_content| {
7870 language_settings_field(settings_content, |language| {
7871 language.allow_rewrap.as_ref()
7872 })
7873 },
7874 write: |settings_content, value| {
7875 language_settings_field_mut(settings_content, value, |language, value| {
7876 language.allow_rewrap = value;
7877 })
7878 },
7879 }),
7880 metadata: None,
7881 files: USER | PROJECT,
7882 }),
7883 ]
7884 }
7885
7886 fn indent_guides_section() -> [SettingsPageItem; 6] {
7887 [
7888 SettingsPageItem::SectionHeader("Indent Guides"),
7889 SettingsPageItem::SettingItem(SettingItem {
7890 title: "Enabled",
7891 description: "Display indent guides in the editor.",
7892 field: Box::new(SettingField {
7893 json_path: Some("languages.$(language).indent_guides.enabled"),
7894 pick: |settings_content| {
7895 language_settings_field(settings_content, |language| {
7896 language
7897 .indent_guides
7898 .as_ref()
7899 .and_then(|indent_guides| indent_guides.enabled.as_ref())
7900 })
7901 },
7902 write: |settings_content, value| {
7903 language_settings_field_mut(settings_content, value, |language, value| {
7904 language.indent_guides.get_or_insert_default().enabled = value;
7905 })
7906 },
7907 }),
7908 metadata: None,
7909 files: USER | PROJECT,
7910 }),
7911 SettingsPageItem::SettingItem(SettingItem {
7912 title: "Line Width",
7913 description: "The width of the indent guides in pixels, between 1 and 10.",
7914 field: Box::new(SettingField {
7915 json_path: Some("languages.$(language).indent_guides.line_width"),
7916 pick: |settings_content| {
7917 language_settings_field(settings_content, |language| {
7918 language
7919 .indent_guides
7920 .as_ref()
7921 .and_then(|indent_guides| indent_guides.line_width.as_ref())
7922 })
7923 },
7924 write: |settings_content, value| {
7925 language_settings_field_mut(settings_content, value, |language, value| {
7926 language.indent_guides.get_or_insert_default().line_width = value;
7927 })
7928 },
7929 }),
7930 metadata: None,
7931 files: USER | PROJECT,
7932 }),
7933 SettingsPageItem::SettingItem(SettingItem {
7934 title: "Active Line Width",
7935 description: "The width of the active indent guide in pixels, between 1 and 10.",
7936 field: Box::new(SettingField {
7937 json_path: Some("languages.$(language).indent_guides.active_line_width"),
7938 pick: |settings_content| {
7939 language_settings_field(settings_content, |language| {
7940 language
7941 .indent_guides
7942 .as_ref()
7943 .and_then(|indent_guides| indent_guides.active_line_width.as_ref())
7944 })
7945 },
7946 write: |settings_content, value| {
7947 language_settings_field_mut(settings_content, value, |language, value| {
7948 language
7949 .indent_guides
7950 .get_or_insert_default()
7951 .active_line_width = value;
7952 })
7953 },
7954 }),
7955 metadata: None,
7956 files: USER | PROJECT,
7957 }),
7958 SettingsPageItem::SettingItem(SettingItem {
7959 title: "Coloring",
7960 description: "Determines how indent guides are colored.",
7961 field: Box::new(SettingField {
7962 json_path: Some("languages.$(language).indent_guides.coloring"),
7963 pick: |settings_content| {
7964 language_settings_field(settings_content, |language| {
7965 language
7966 .indent_guides
7967 .as_ref()
7968 .and_then(|indent_guides| indent_guides.coloring.as_ref())
7969 })
7970 },
7971 write: |settings_content, value| {
7972 language_settings_field_mut(settings_content, value, |language, value| {
7973 language.indent_guides.get_or_insert_default().coloring = value;
7974 })
7975 },
7976 }),
7977 metadata: None,
7978 files: USER | PROJECT,
7979 }),
7980 SettingsPageItem::SettingItem(SettingItem {
7981 title: "Background Coloring",
7982 description: "Determines how indent guide backgrounds are colored.",
7983 field: Box::new(SettingField {
7984 json_path: Some("languages.$(language).indent_guides.background_coloring"),
7985 pick: |settings_content| {
7986 language_settings_field(settings_content, |language| {
7987 language.indent_guides.as_ref().and_then(|indent_guides| {
7988 indent_guides.background_coloring.as_ref()
7989 })
7990 })
7991 },
7992 write: |settings_content, value| {
7993 language_settings_field_mut(settings_content, value, |language, value| {
7994 language
7995 .indent_guides
7996 .get_or_insert_default()
7997 .background_coloring = value;
7998 })
7999 },
8000 }),
8001 metadata: None,
8002 files: USER | PROJECT,
8003 }),
8004 ]
8005 }
8006
8007 fn formatting_section() -> [SettingsPageItem; 7] {
8008 [
8009 SettingsPageItem::SectionHeader("Formatting"),
8010 SettingsPageItem::SettingItem(SettingItem {
8011 title: "Format On Save",
8012 description: "Whether or not to perform a buffer format before saving.",
8013 field: Box::new(
8014 // TODO(settings_ui): this setting should just be a bool
8015 SettingField {
8016 json_path: Some("languages.$(language).format_on_save"),
8017 pick: |settings_content| {
8018 language_settings_field(settings_content, |language| {
8019 language.format_on_save.as_ref()
8020 })
8021 },
8022 write: |settings_content, value| {
8023 language_settings_field_mut(
8024 settings_content,
8025 value,
8026 |language, value| {
8027 language.format_on_save = value;
8028 },
8029 )
8030 },
8031 },
8032 ),
8033 metadata: None,
8034 files: USER | PROJECT,
8035 }),
8036 SettingsPageItem::SettingItem(SettingItem {
8037 title: "Remove Trailing Whitespace On Save",
8038 description: "Whether or not to remove any trailing whitespace from lines of a buffer before saving it.",
8039 field: Box::new(SettingField {
8040 json_path: Some("languages.$(language).remove_trailing_whitespace_on_save"),
8041 pick: |settings_content| {
8042 language_settings_field(settings_content, |language| {
8043 language.remove_trailing_whitespace_on_save.as_ref()
8044 })
8045 },
8046 write: |settings_content, value| {
8047 language_settings_field_mut(settings_content, value, |language, value| {
8048 language.remove_trailing_whitespace_on_save = value;
8049 })
8050 },
8051 }),
8052 metadata: None,
8053 files: USER | PROJECT,
8054 }),
8055 SettingsPageItem::SettingItem(SettingItem {
8056 title: "Ensure Final Newline On Save",
8057 description: "Whether or not to ensure there's a single newline at the end of a buffer when saving it.",
8058 field: Box::new(SettingField {
8059 json_path: Some("languages.$(language).ensure_final_newline_on_save"),
8060 pick: |settings_content| {
8061 language_settings_field(settings_content, |language| {
8062 language.ensure_final_newline_on_save.as_ref()
8063 })
8064 },
8065 write: |settings_content, value| {
8066 language_settings_field_mut(settings_content, value, |language, value| {
8067 language.ensure_final_newline_on_save = value;
8068 })
8069 },
8070 }),
8071 metadata: None,
8072 files: USER | PROJECT,
8073 }),
8074 SettingsPageItem::SettingItem(SettingItem {
8075 title: "Formatter",
8076 description: "How to perform a buffer format.",
8077 field: Box::new(
8078 SettingField {
8079 json_path: Some("languages.$(language).formatter"),
8080 pick: |settings_content| {
8081 language_settings_field(settings_content, |language| {
8082 language.formatter.as_ref()
8083 })
8084 },
8085 write: |settings_content, value| {
8086 language_settings_field_mut(
8087 settings_content,
8088 value,
8089 |language, value| {
8090 language.formatter = value;
8091 },
8092 )
8093 },
8094 }
8095 .unimplemented(),
8096 ),
8097 metadata: None,
8098 files: USER | PROJECT,
8099 }),
8100 SettingsPageItem::SettingItem(SettingItem {
8101 title: "Use On Type Format",
8102 description: "Whether to use additional LSP queries to format (and amend) the code after every \"trigger\" symbol input, defined by LSP server capabilities",
8103 field: Box::new(SettingField {
8104 json_path: Some("languages.$(language).use_on_type_format"),
8105 pick: |settings_content| {
8106 language_settings_field(settings_content, |language| {
8107 language.use_on_type_format.as_ref()
8108 })
8109 },
8110 write: |settings_content, value| {
8111 language_settings_field_mut(settings_content, value, |language, value| {
8112 language.use_on_type_format = value;
8113 })
8114 },
8115 }),
8116 metadata: None,
8117 files: USER | PROJECT,
8118 }),
8119 SettingsPageItem::SettingItem(SettingItem {
8120 title: "Code Actions On Format",
8121 description: "Additional code actions to run when formatting.",
8122 field: Box::new(
8123 SettingField {
8124 json_path: Some("languages.$(language).code_actions_on_format"),
8125 pick: |settings_content| {
8126 language_settings_field(settings_content, |language| {
8127 language.code_actions_on_format.as_ref()
8128 })
8129 },
8130 write: |settings_content, value| {
8131 language_settings_field_mut(
8132 settings_content,
8133 value,
8134 |language, value| {
8135 language.code_actions_on_format = value;
8136 },
8137 )
8138 },
8139 }
8140 .unimplemented(),
8141 ),
8142 metadata: None,
8143 files: USER | PROJECT,
8144 }),
8145 ]
8146 }
8147
8148 fn autoclose_section() -> [SettingsPageItem; 5] {
8149 [
8150 SettingsPageItem::SectionHeader("Autoclose"),
8151 SettingsPageItem::SettingItem(SettingItem {
8152 title: "Use Autoclose",
8153 description: "Whether to automatically type closing characters for you. For example, when you type '(', Zed will automatically add a closing ')' at the correct position.",
8154 field: Box::new(SettingField {
8155 json_path: Some("languages.$(language).use_autoclose"),
8156 pick: |settings_content| {
8157 language_settings_field(settings_content, |language| {
8158 language.use_autoclose.as_ref()
8159 })
8160 },
8161 write: |settings_content, value| {
8162 language_settings_field_mut(settings_content, value, |language, value| {
8163 language.use_autoclose = value;
8164 })
8165 },
8166 }),
8167 metadata: None,
8168 files: USER | PROJECT,
8169 }),
8170 SettingsPageItem::SettingItem(SettingItem {
8171 title: "Use Auto Surround",
8172 description: "Whether to automatically surround text with characters for you. For example, when you select text and type '(', Zed will automatically surround text with ().",
8173 field: Box::new(SettingField {
8174 json_path: Some("languages.$(language).use_auto_surround"),
8175 pick: |settings_content| {
8176 language_settings_field(settings_content, |language| {
8177 language.use_auto_surround.as_ref()
8178 })
8179 },
8180 write: |settings_content, value| {
8181 language_settings_field_mut(settings_content, value, |language, value| {
8182 language.use_auto_surround = value;
8183 })
8184 },
8185 }),
8186 metadata: None,
8187 files: USER | PROJECT,
8188 }),
8189 SettingsPageItem::SettingItem(SettingItem {
8190 title: "Always Treat Brackets As Autoclosed",
8191 description: "Controls whether the closing characters are always skipped over and auto-removed no matter how they were inserted.",
8192 field: Box::new(SettingField {
8193 json_path: Some("languages.$(language).always_treat_brackets_as_autoclosed"),
8194 pick: |settings_content| {
8195 language_settings_field(settings_content, |language| {
8196 language.always_treat_brackets_as_autoclosed.as_ref()
8197 })
8198 },
8199 write: |settings_content, value| {
8200 language_settings_field_mut(settings_content, value, |language, value| {
8201 language.always_treat_brackets_as_autoclosed = value;
8202 })
8203 },
8204 }),
8205 metadata: None,
8206 files: USER | PROJECT,
8207 }),
8208 SettingsPageItem::SettingItem(SettingItem {
8209 title: "JSX Tag Auto Close",
8210 description: "Whether to automatically close JSX tags.",
8211 field: Box::new(SettingField {
8212 json_path: Some("languages.$(language).jsx_tag_auto_close"),
8213 // TODO(settings_ui): this setting should just be a bool
8214 pick: |settings_content| {
8215 language_settings_field(settings_content, |language| {
8216 language.jsx_tag_auto_close.as_ref()?.enabled.as_ref()
8217 })
8218 },
8219 write: |settings_content, value| {
8220 language_settings_field_mut(settings_content, value, |language, value| {
8221 language.jsx_tag_auto_close.get_or_insert_default().enabled = value;
8222 })
8223 },
8224 }),
8225 metadata: None,
8226 files: USER | PROJECT,
8227 }),
8228 ]
8229 }
8230
8231 fn whitespace_section() -> [SettingsPageItem; 4] {
8232 [
8233 SettingsPageItem::SectionHeader("Whitespace"),
8234 SettingsPageItem::SettingItem(SettingItem {
8235 title: "Show Whitespaces",
8236 description: "Whether to show tabs and spaces in the editor.",
8237 field: Box::new(SettingField {
8238 json_path: Some("languages.$(language).show_whitespaces"),
8239 pick: |settings_content| {
8240 language_settings_field(settings_content, |language| {
8241 language.show_whitespaces.as_ref()
8242 })
8243 },
8244 write: |settings_content, value| {
8245 language_settings_field_mut(settings_content, value, |language, value| {
8246 language.show_whitespaces = value;
8247 })
8248 },
8249 }),
8250 metadata: None,
8251 files: USER | PROJECT,
8252 }),
8253 SettingsPageItem::SettingItem(SettingItem {
8254 title: "Space Whitespace Indicator",
8255 description: "Visible character used to render space characters when show_whitespaces is enabled (default: \"•\")",
8256 field: Box::new(
8257 SettingField {
8258 json_path: Some("languages.$(language).whitespace_map.space"),
8259 pick: |settings_content| {
8260 language_settings_field(settings_content, |language| {
8261 language.whitespace_map.as_ref()?.space.as_ref()
8262 })
8263 },
8264 write: |settings_content, value| {
8265 language_settings_field_mut(
8266 settings_content,
8267 value,
8268 |language, value| {
8269 language.whitespace_map.get_or_insert_default().space = value;
8270 },
8271 )
8272 },
8273 }
8274 .unimplemented(),
8275 ),
8276 metadata: None,
8277 files: USER | PROJECT,
8278 }),
8279 SettingsPageItem::SettingItem(SettingItem {
8280 title: "Tab Whitespace Indicator",
8281 description: "Visible character used to render tab characters when show_whitespaces is enabled (default: \"→\")",
8282 field: Box::new(
8283 SettingField {
8284 json_path: Some("languages.$(language).whitespace_map.tab"),
8285 pick: |settings_content| {
8286 language_settings_field(settings_content, |language| {
8287 language.whitespace_map.as_ref()?.tab.as_ref()
8288 })
8289 },
8290 write: |settings_content, value| {
8291 language_settings_field_mut(
8292 settings_content,
8293 value,
8294 |language, value| {
8295 language.whitespace_map.get_or_insert_default().tab = value;
8296 },
8297 )
8298 },
8299 }
8300 .unimplemented(),
8301 ),
8302 metadata: None,
8303 files: USER | PROJECT,
8304 }),
8305 ]
8306 }
8307
8308 fn completions_section() -> [SettingsPageItem; 7] {
8309 [
8310 SettingsPageItem::SectionHeader("Completions"),
8311 SettingsPageItem::SettingItem(SettingItem {
8312 title: "Show Completions On Input",
8313 description: "Whether to pop the completions menu while typing in an editor without explicitly requesting it.",
8314 field: Box::new(SettingField {
8315 json_path: Some("languages.$(language).show_completions_on_input"),
8316 pick: |settings_content| {
8317 language_settings_field(settings_content, |language| {
8318 language.show_completions_on_input.as_ref()
8319 })
8320 },
8321 write: |settings_content, value| {
8322 language_settings_field_mut(settings_content, value, |language, value| {
8323 language.show_completions_on_input = value;
8324 })
8325 },
8326 }),
8327 metadata: None,
8328 files: USER | PROJECT,
8329 }),
8330 SettingsPageItem::SettingItem(SettingItem {
8331 title: "Show Completion Documentation",
8332 description: "Whether to display inline and alongside documentation for items in the completions menu.",
8333 field: Box::new(SettingField {
8334 json_path: Some("languages.$(language).show_completion_documentation"),
8335 pick: |settings_content| {
8336 language_settings_field(settings_content, |language| {
8337 language.show_completion_documentation.as_ref()
8338 })
8339 },
8340 write: |settings_content, value| {
8341 language_settings_field_mut(settings_content, value, |language, value| {
8342 language.show_completion_documentation = value;
8343 })
8344 },
8345 }),
8346 metadata: None,
8347 files: USER | PROJECT,
8348 }),
8349 SettingsPageItem::SettingItem(SettingItem {
8350 title: "Words",
8351 description: "Controls how words are completed.",
8352 field: Box::new(SettingField {
8353 json_path: Some("languages.$(language).completions.words"),
8354 pick: |settings_content| {
8355 language_settings_field(settings_content, |language| {
8356 language.completions.as_ref()?.words.as_ref()
8357 })
8358 },
8359 write: |settings_content, value| {
8360 language_settings_field_mut(settings_content, value, |language, value| {
8361 language.completions.get_or_insert_default().words = value;
8362 })
8363 },
8364 }),
8365 metadata: None,
8366 files: USER | PROJECT,
8367 }),
8368 SettingsPageItem::SettingItem(SettingItem {
8369 title: "Words Min Length",
8370 description: "How many characters has to be in the completions query to automatically show the words-based completions.",
8371 field: Box::new(SettingField {
8372 json_path: Some("languages.$(language).completions.words_min_length"),
8373 pick: |settings_content| {
8374 language_settings_field(settings_content, |language| {
8375 language.completions.as_ref()?.words_min_length.as_ref()
8376 })
8377 },
8378 write: |settings_content, value| {
8379 language_settings_field_mut(settings_content, value, |language, value| {
8380 language
8381 .completions
8382 .get_or_insert_default()
8383 .words_min_length = value;
8384 })
8385 },
8386 }),
8387 metadata: None,
8388 files: USER | PROJECT,
8389 }),
8390 SettingsPageItem::SettingItem(SettingItem {
8391 title: "Completion Menu Scrollbar",
8392 description: "When to show the scrollbar in the completion menu.",
8393 field: Box::new(SettingField {
8394 json_path: Some("editor.completion_menu_scrollbar"),
8395 pick: |settings_content| {
8396 settings_content.editor.completion_menu_scrollbar.as_ref()
8397 },
8398 write: |settings_content, value| {
8399 settings_content.editor.completion_menu_scrollbar = value;
8400 },
8401 }),
8402 metadata: None,
8403 files: USER,
8404 }),
8405 SettingsPageItem::SettingItem(SettingItem {
8406 title: "Completion Detail Alignment",
8407 description: "Whether to align detail text in code completions context menus left or right.",
8408 field: Box::new(SettingField {
8409 json_path: Some("editor.completion_detail_alignment"),
8410 pick: |settings_content| {
8411 settings_content.editor.completion_detail_alignment.as_ref()
8412 },
8413 write: |settings_content, value| {
8414 settings_content.editor.completion_detail_alignment = value;
8415 },
8416 }),
8417 metadata: None,
8418 files: USER,
8419 }),
8420 ]
8421 }
8422
8423 fn inlay_hints_section() -> [SettingsPageItem; 10] {
8424 [
8425 SettingsPageItem::SectionHeader("Inlay Hints"),
8426 SettingsPageItem::SettingItem(SettingItem {
8427 title: "Enabled",
8428 description: "Global switch to toggle hints on and off.",
8429 field: Box::new(SettingField {
8430 json_path: Some("languages.$(language).inlay_hints.enabled"),
8431 pick: |settings_content| {
8432 language_settings_field(settings_content, |language| {
8433 language.inlay_hints.as_ref()?.enabled.as_ref()
8434 })
8435 },
8436 write: |settings_content, value| {
8437 language_settings_field_mut(settings_content, value, |language, value| {
8438 language.inlay_hints.get_or_insert_default().enabled = value;
8439 })
8440 },
8441 }),
8442 metadata: None,
8443 files: USER | PROJECT,
8444 }),
8445 SettingsPageItem::SettingItem(SettingItem {
8446 title: "Show Value Hints",
8447 description: "Global switch to toggle inline values on and off when debugging.",
8448 field: Box::new(SettingField {
8449 json_path: Some("languages.$(language).inlay_hints.show_value_hints"),
8450 pick: |settings_content| {
8451 language_settings_field(settings_content, |language| {
8452 language.inlay_hints.as_ref()?.show_value_hints.as_ref()
8453 })
8454 },
8455 write: |settings_content, value| {
8456 language_settings_field_mut(settings_content, value, |language, value| {
8457 language
8458 .inlay_hints
8459 .get_or_insert_default()
8460 .show_value_hints = value;
8461 })
8462 },
8463 }),
8464 metadata: None,
8465 files: USER | PROJECT,
8466 }),
8467 SettingsPageItem::SettingItem(SettingItem {
8468 title: "Show Type Hints",
8469 description: "Whether type hints should be shown.",
8470 field: Box::new(SettingField {
8471 json_path: Some("languages.$(language).inlay_hints.show_type_hints"),
8472 pick: |settings_content| {
8473 language_settings_field(settings_content, |language| {
8474 language.inlay_hints.as_ref()?.show_type_hints.as_ref()
8475 })
8476 },
8477 write: |settings_content, value| {
8478 language_settings_field_mut(settings_content, value, |language, value| {
8479 language.inlay_hints.get_or_insert_default().show_type_hints = value;
8480 })
8481 },
8482 }),
8483 metadata: None,
8484 files: USER | PROJECT,
8485 }),
8486 SettingsPageItem::SettingItem(SettingItem {
8487 title: "Show Parameter Hints",
8488 description: "Whether parameter hints should be shown.",
8489 field: Box::new(SettingField {
8490 json_path: Some("languages.$(language).inlay_hints.show_parameter_hints"),
8491 pick: |settings_content| {
8492 language_settings_field(settings_content, |language| {
8493 language.inlay_hints.as_ref()?.show_parameter_hints.as_ref()
8494 })
8495 },
8496 write: |settings_content, value| {
8497 language_settings_field_mut(settings_content, value, |language, value| {
8498 language
8499 .inlay_hints
8500 .get_or_insert_default()
8501 .show_parameter_hints = value;
8502 })
8503 },
8504 }),
8505 metadata: None,
8506 files: USER | PROJECT,
8507 }),
8508 SettingsPageItem::SettingItem(SettingItem {
8509 title: "Show Other Hints",
8510 description: "Whether other hints should be shown.",
8511 field: Box::new(SettingField {
8512 json_path: Some("languages.$(language).inlay_hints.show_other_hints"),
8513 pick: |settings_content| {
8514 language_settings_field(settings_content, |language| {
8515 language.inlay_hints.as_ref()?.show_other_hints.as_ref()
8516 })
8517 },
8518 write: |settings_content, value| {
8519 language_settings_field_mut(settings_content, value, |language, value| {
8520 language
8521 .inlay_hints
8522 .get_or_insert_default()
8523 .show_other_hints = value;
8524 })
8525 },
8526 }),
8527 metadata: None,
8528 files: USER | PROJECT,
8529 }),
8530 SettingsPageItem::SettingItem(SettingItem {
8531 title: "Show Background",
8532 description: "Show a background for inlay hints.",
8533 field: Box::new(SettingField {
8534 json_path: Some("languages.$(language).inlay_hints.show_background"),
8535 pick: |settings_content| {
8536 language_settings_field(settings_content, |language| {
8537 language.inlay_hints.as_ref()?.show_background.as_ref()
8538 })
8539 },
8540 write: |settings_content, value| {
8541 language_settings_field_mut(settings_content, value, |language, value| {
8542 language.inlay_hints.get_or_insert_default().show_background = value;
8543 })
8544 },
8545 }),
8546 metadata: None,
8547 files: USER | PROJECT,
8548 }),
8549 SettingsPageItem::SettingItem(SettingItem {
8550 title: "Edit Debounce Ms",
8551 description: "Whether or not to debounce inlay hints updates after buffer edits (set to 0 to disable debouncing).",
8552 field: Box::new(SettingField {
8553 json_path: Some("languages.$(language).inlay_hints.edit_debounce_ms"),
8554 pick: |settings_content| {
8555 language_settings_field(settings_content, |language| {
8556 language.inlay_hints.as_ref()?.edit_debounce_ms.as_ref()
8557 })
8558 },
8559 write: |settings_content, value| {
8560 language_settings_field_mut(settings_content, value, |language, value| {
8561 language
8562 .inlay_hints
8563 .get_or_insert_default()
8564 .edit_debounce_ms = value;
8565 })
8566 },
8567 }),
8568 metadata: None,
8569 files: USER | PROJECT,
8570 }),
8571 SettingsPageItem::SettingItem(SettingItem {
8572 title: "Scroll Debounce Ms",
8573 description: "Whether or not to debounce inlay hints updates after buffer scrolls (set to 0 to disable debouncing).",
8574 field: Box::new(SettingField {
8575 json_path: Some("languages.$(language).inlay_hints.scroll_debounce_ms"),
8576 pick: |settings_content| {
8577 language_settings_field(settings_content, |language| {
8578 language.inlay_hints.as_ref()?.scroll_debounce_ms.as_ref()
8579 })
8580 },
8581 write: |settings_content, value| {
8582 language_settings_field_mut(settings_content, value, |language, value| {
8583 language
8584 .inlay_hints
8585 .get_or_insert_default()
8586 .scroll_debounce_ms = value;
8587 })
8588 },
8589 }),
8590 metadata: None,
8591 files: USER | PROJECT,
8592 }),
8593 SettingsPageItem::SettingItem(SettingItem {
8594 title: "Toggle On Modifiers Press",
8595 description: "Toggles inlay hints (hides or shows) when the user presses the modifiers specified.",
8596 field: Box::new(
8597 SettingField {
8598 json_path: Some(
8599 "languages.$(language).inlay_hints.toggle_on_modifiers_press",
8600 ),
8601 pick: |settings_content| {
8602 language_settings_field(settings_content, |language| {
8603 language
8604 .inlay_hints
8605 .as_ref()?
8606 .toggle_on_modifiers_press
8607 .as_ref()
8608 })
8609 },
8610 write: |settings_content, value| {
8611 language_settings_field_mut(
8612 settings_content,
8613 value,
8614 |language, value| {
8615 language
8616 .inlay_hints
8617 .get_or_insert_default()
8618 .toggle_on_modifiers_press = value;
8619 },
8620 )
8621 },
8622 }
8623 .unimplemented(),
8624 ),
8625 metadata: None,
8626 files: USER | PROJECT,
8627 }),
8628 ]
8629 }
8630
8631 fn tasks_section() -> [SettingsPageItem; 4] {
8632 [
8633 SettingsPageItem::SectionHeader("Tasks"),
8634 SettingsPageItem::SettingItem(SettingItem {
8635 title: "Enabled",
8636 description: "Whether tasks are enabled for this language.",
8637 field: Box::new(SettingField {
8638 json_path: Some("languages.$(language).tasks.enabled"),
8639 pick: |settings_content| {
8640 language_settings_field(settings_content, |language| {
8641 language.tasks.as_ref()?.enabled.as_ref()
8642 })
8643 },
8644 write: |settings_content, value| {
8645 language_settings_field_mut(settings_content, value, |language, value| {
8646 language.tasks.get_or_insert_default().enabled = value;
8647 })
8648 },
8649 }),
8650 metadata: None,
8651 files: USER | PROJECT,
8652 }),
8653 SettingsPageItem::SettingItem(SettingItem {
8654 title: "Variables",
8655 description: "Extra task variables to set for a particular language.",
8656 field: Box::new(
8657 SettingField {
8658 json_path: Some("languages.$(language).tasks.variables"),
8659 pick: |settings_content| {
8660 language_settings_field(settings_content, |language| {
8661 language.tasks.as_ref()?.variables.as_ref()
8662 })
8663 },
8664 write: |settings_content, value| {
8665 language_settings_field_mut(
8666 settings_content,
8667 value,
8668 |language, value| {
8669 language.tasks.get_or_insert_default().variables = value;
8670 },
8671 )
8672 },
8673 }
8674 .unimplemented(),
8675 ),
8676 metadata: None,
8677 files: USER | PROJECT,
8678 }),
8679 SettingsPageItem::SettingItem(SettingItem {
8680 title: "Prefer LSP",
8681 description: "Use LSP tasks over Zed language extension tasks.",
8682 field: Box::new(SettingField {
8683 json_path: Some("languages.$(language).tasks.prefer_lsp"),
8684 pick: |settings_content| {
8685 language_settings_field(settings_content, |language| {
8686 language.tasks.as_ref()?.prefer_lsp.as_ref()
8687 })
8688 },
8689 write: |settings_content, value| {
8690 language_settings_field_mut(settings_content, value, |language, value| {
8691 language.tasks.get_or_insert_default().prefer_lsp = value;
8692 })
8693 },
8694 }),
8695 metadata: None,
8696 files: USER | PROJECT,
8697 }),
8698 ]
8699 }
8700
8701 fn miscellaneous_section() -> [SettingsPageItem; 7] {
8702 [
8703 SettingsPageItem::SectionHeader("Miscellaneous"),
8704 SettingsPageItem::SettingItem(SettingItem {
8705 title: "Word Diff Enabled",
8706 description: "Whether to enable word diff highlighting in the editor. When enabled, changed words within modified lines are highlighted to show exactly what changed.",
8707 field: Box::new(SettingField {
8708 json_path: Some("languages.$(language).word_diff_enabled"),
8709 pick: |settings_content| {
8710 language_settings_field(settings_content, |language| {
8711 language.word_diff_enabled.as_ref()
8712 })
8713 },
8714 write: |settings_content, value| {
8715 language_settings_field_mut(settings_content, value, |language, value| {
8716 language.word_diff_enabled = value;
8717 })
8718 },
8719 }),
8720 metadata: None,
8721 files: USER | PROJECT,
8722 }),
8723 SettingsPageItem::SettingItem(SettingItem {
8724 title: "Debuggers",
8725 description: "Preferred debuggers for this language.",
8726 field: Box::new(
8727 SettingField {
8728 json_path: Some("languages.$(language).debuggers"),
8729 pick: |settings_content| {
8730 language_settings_field(settings_content, |language| {
8731 language.debuggers.as_ref()
8732 })
8733 },
8734 write: |settings_content, value| {
8735 language_settings_field_mut(
8736 settings_content,
8737 value,
8738 |language, value| {
8739 language.debuggers = value;
8740 },
8741 )
8742 },
8743 }
8744 .unimplemented(),
8745 ),
8746 metadata: None,
8747 files: USER | PROJECT,
8748 }),
8749 SettingsPageItem::SettingItem(SettingItem {
8750 title: "Middle Click Paste",
8751 description: "Enable middle-click paste on Linux.",
8752 field: Box::new(SettingField {
8753 json_path: Some("languages.$(language).editor.middle_click_paste"),
8754 pick: |settings_content| settings_content.editor.middle_click_paste.as_ref(),
8755 write: |settings_content, value| {
8756 settings_content.editor.middle_click_paste = value;
8757 },
8758 }),
8759 metadata: None,
8760 files: USER,
8761 }),
8762 SettingsPageItem::SettingItem(SettingItem {
8763 title: "Extend Comment On Newline",
8764 description: "Whether to start a new line with a comment when a previous line is a comment as well.",
8765 field: Box::new(SettingField {
8766 json_path: Some("languages.$(language).extend_comment_on_newline"),
8767 pick: |settings_content| {
8768 language_settings_field(settings_content, |language| {
8769 language.extend_comment_on_newline.as_ref()
8770 })
8771 },
8772 write: |settings_content, value| {
8773 language_settings_field_mut(settings_content, value, |language, value| {
8774 language.extend_comment_on_newline = value;
8775 })
8776 },
8777 }),
8778 metadata: None,
8779 files: USER | PROJECT,
8780 }),
8781 SettingsPageItem::SettingItem(SettingItem {
8782 title: "Colorize Brackets",
8783 description: "Whether to colorize brackets in the editor.",
8784 field: Box::new(SettingField {
8785 json_path: Some("languages.$(language).colorize_brackets"),
8786 pick: |settings_content| {
8787 language_settings_field(settings_content, |language| {
8788 language.colorize_brackets.as_ref()
8789 })
8790 },
8791 write: |settings_content, value| {
8792 language_settings_field_mut(settings_content, value, |language, value| {
8793 language.colorize_brackets = value;
8794 })
8795 },
8796 }),
8797 metadata: None,
8798 files: USER | PROJECT,
8799 }),
8800 SettingsPageItem::SettingItem(SettingItem {
8801 title: "Vim/Emacs Modeline Support",
8802 description: "Number of lines to search for modelines (set to 0 to disable).",
8803 field: Box::new(SettingField {
8804 json_path: Some("modeline_lines"),
8805 pick: |settings_content| settings_content.modeline_lines.as_ref(),
8806 write: |settings_content, value| {
8807 settings_content.modeline_lines = value;
8808 },
8809 }),
8810 metadata: None,
8811 files: USER | PROJECT,
8812 }),
8813 ]
8814 }
8815
8816 fn global_only_miscellaneous_sub_section() -> [SettingsPageItem; 3] {
8817 [
8818 SettingsPageItem::SettingItem(SettingItem {
8819 title: "Image Viewer",
8820 description: "The unit for image file sizes.",
8821 field: Box::new(SettingField {
8822 json_path: Some("image_viewer.unit"),
8823 pick: |settings_content| {
8824 settings_content
8825 .image_viewer
8826 .as_ref()
8827 .and_then(|image_viewer| image_viewer.unit.as_ref())
8828 },
8829 write: |settings_content, value| {
8830 settings_content.image_viewer.get_or_insert_default().unit = value;
8831 },
8832 }),
8833 metadata: None,
8834 files: USER,
8835 }),
8836 SettingsPageItem::SettingItem(SettingItem {
8837 title: "Auto Replace Emoji Shortcode",
8838 description: "Whether to automatically replace emoji shortcodes with emoji characters.",
8839 field: Box::new(SettingField {
8840 json_path: Some("message_editor.auto_replace_emoji_shortcode"),
8841 pick: |settings_content| {
8842 settings_content
8843 .message_editor
8844 .as_ref()
8845 .and_then(|message_editor| {
8846 message_editor.auto_replace_emoji_shortcode.as_ref()
8847 })
8848 },
8849 write: |settings_content, value| {
8850 settings_content
8851 .message_editor
8852 .get_or_insert_default()
8853 .auto_replace_emoji_shortcode = value;
8854 },
8855 }),
8856 metadata: None,
8857 files: USER,
8858 }),
8859 SettingsPageItem::SettingItem(SettingItem {
8860 title: "Drop Size Target",
8861 description: "Relative size of the drop target in the editor that will open dropped file as a split pane.",
8862 field: Box::new(SettingField {
8863 json_path: Some("drop_target_size"),
8864 pick: |settings_content| settings_content.workspace.drop_target_size.as_ref(),
8865 write: |settings_content, value| {
8866 settings_content.workspace.drop_target_size = value;
8867 },
8868 }),
8869 metadata: None,
8870 files: USER,
8871 }),
8872 ]
8873 }
8874
8875 let is_global = active_language().is_none();
8876
8877 let lsp_document_colors_item = [SettingsPageItem::SettingItem(SettingItem {
8878 title: "LSP Document Colors",
8879 description: "How to render LSP color previews in the editor.",
8880 field: Box::new(SettingField {
8881 json_path: Some("lsp_document_colors"),
8882 pick: |settings_content| settings_content.editor.lsp_document_colors.as_ref(),
8883 write: |settings_content, value| {
8884 settings_content.editor.lsp_document_colors = value;
8885 },
8886 }),
8887 metadata: None,
8888 files: USER,
8889 })];
8890
8891 if is_global {
8892 concat_sections!(
8893 indentation_section(),
8894 wrapping_section(),
8895 indent_guides_section(),
8896 formatting_section(),
8897 autoclose_section(),
8898 whitespace_section(),
8899 completions_section(),
8900 inlay_hints_section(),
8901 lsp_document_colors_item,
8902 tasks_section(),
8903 miscellaneous_section(),
8904 global_only_miscellaneous_sub_section(),
8905 )
8906 } else {
8907 concat_sections!(
8908 indentation_section(),
8909 wrapping_section(),
8910 indent_guides_section(),
8911 formatting_section(),
8912 autoclose_section(),
8913 whitespace_section(),
8914 completions_section(),
8915 inlay_hints_section(),
8916 tasks_section(),
8917 miscellaneous_section(),
8918 )
8919 }
8920}
8921
8922/// LanguageSettings items that should be included in the "Languages & Tools" page
8923/// not the "Editor" page
8924fn non_editor_language_settings_data() -> Box<[SettingsPageItem]> {
8925 fn lsp_section() -> [SettingsPageItem; 8] {
8926 [
8927 SettingsPageItem::SectionHeader("LSP"),
8928 SettingsPageItem::SettingItem(SettingItem {
8929 title: "Enable Language Server",
8930 description: "Whether to use language servers to provide code intelligence.",
8931 field: Box::new(SettingField {
8932 json_path: Some("languages.$(language).enable_language_server"),
8933 pick: |settings_content| {
8934 language_settings_field(settings_content, |language| {
8935 language.enable_language_server.as_ref()
8936 })
8937 },
8938 write: |settings_content, value| {
8939 language_settings_field_mut(settings_content, value, |language, value| {
8940 language.enable_language_server = value;
8941 })
8942 },
8943 }),
8944 metadata: None,
8945 files: USER | PROJECT,
8946 }),
8947 SettingsPageItem::SettingItem(SettingItem {
8948 title: "Language Servers",
8949 description: "The list of language servers to use (or disable) for this language.",
8950 field: Box::new(
8951 SettingField {
8952 json_path: Some("languages.$(language).language_servers"),
8953 pick: |settings_content| {
8954 language_settings_field(settings_content, |language| {
8955 language.language_servers.as_ref()
8956 })
8957 },
8958 write: |settings_content, value| {
8959 language_settings_field_mut(
8960 settings_content,
8961 value,
8962 |language, value| {
8963 language.language_servers = value;
8964 },
8965 )
8966 },
8967 }
8968 .unimplemented(),
8969 ),
8970 metadata: None,
8971 files: USER | PROJECT,
8972 }),
8973 SettingsPageItem::SettingItem(SettingItem {
8974 title: "Linked Edits",
8975 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.",
8976 field: Box::new(SettingField {
8977 json_path: Some("languages.$(language).linked_edits"),
8978 pick: |settings_content| {
8979 language_settings_field(settings_content, |language| {
8980 language.linked_edits.as_ref()
8981 })
8982 },
8983 write: |settings_content, value| {
8984 language_settings_field_mut(settings_content, value, |language, value| {
8985 language.linked_edits = value;
8986 })
8987 },
8988 }),
8989 metadata: None,
8990 files: USER | PROJECT,
8991 }),
8992 SettingsPageItem::SettingItem(SettingItem {
8993 title: "Go To Definition Fallback",
8994 description: "Whether to follow-up empty Go to definition responses from the language server.",
8995 field: Box::new(SettingField {
8996 json_path: Some("go_to_definition_fallback"),
8997 pick: |settings_content| {
8998 settings_content.editor.go_to_definition_fallback.as_ref()
8999 },
9000 write: |settings_content, value| {
9001 settings_content.editor.go_to_definition_fallback = value;
9002 },
9003 }),
9004 metadata: None,
9005 files: USER,
9006 }),
9007 SettingsPageItem::SettingItem(SettingItem {
9008 title: "Semantic Tokens",
9009 description: {
9010 static DESCRIPTION: OnceLock<&'static str> = OnceLock::new();
9011 DESCRIPTION.get_or_init(|| {
9012 SemanticTokens::VARIANTS
9013 .iter()
9014 .filter_map(|v| {
9015 v.get_documentation().map(|doc| format!("{v:?}: {doc}"))
9016 })
9017 .join("\n")
9018 .leak()
9019 })
9020 },
9021 field: Box::new(SettingField {
9022 json_path: Some("languages.$(language).semantic_tokens"),
9023 pick: |settings_content| {
9024 settings_content
9025 .project
9026 .all_languages
9027 .defaults
9028 .semantic_tokens
9029 .as_ref()
9030 },
9031 write: |settings_content, value| {
9032 settings_content
9033 .project
9034 .all_languages
9035 .defaults
9036 .semantic_tokens = value;
9037 },
9038 }),
9039 metadata: None,
9040 files: USER | PROJECT,
9041 }),
9042 SettingsPageItem::SettingItem(SettingItem {
9043 title: "LSP Folding Ranges",
9044 description: "When enabled, use folding ranges from the language server instead of indent-based folding.",
9045 field: Box::new(SettingField {
9046 json_path: Some("languages.$(language).document_folding_ranges"),
9047 pick: |settings_content| {
9048 language_settings_field(settings_content, |language| {
9049 language.document_folding_ranges.as_ref()
9050 })
9051 },
9052 write: |settings_content, value| {
9053 language_settings_field_mut(settings_content, value, |language, value| {
9054 language.document_folding_ranges = value;
9055 })
9056 },
9057 }),
9058 metadata: None,
9059 files: USER | PROJECT,
9060 }),
9061 SettingsPageItem::SettingItem(SettingItem {
9062 title: "LSP Document Symbols",
9063 description: "When enabled, use the language server's document symbols for outlines and breadcrumbs instead of tree-sitter.",
9064 field: Box::new(SettingField {
9065 json_path: Some("languages.$(language).document_symbols"),
9066 pick: |settings_content| {
9067 language_settings_field(settings_content, |language| {
9068 language.document_symbols.as_ref()
9069 })
9070 },
9071 write: |settings_content, value| {
9072 language_settings_field_mut(settings_content, value, |language, value| {
9073 language.document_symbols = value;
9074 })
9075 },
9076 }),
9077 metadata: None,
9078 files: USER | PROJECT,
9079 }),
9080 ]
9081 }
9082
9083 fn lsp_completions_section() -> [SettingsPageItem; 4] {
9084 [
9085 SettingsPageItem::SectionHeader("LSP Completions"),
9086 SettingsPageItem::SettingItem(SettingItem {
9087 title: "Enabled",
9088 description: "Whether to fetch LSP completions or not.",
9089 field: Box::new(SettingField {
9090 json_path: Some("languages.$(language).completions.lsp"),
9091 pick: |settings_content| {
9092 language_settings_field(settings_content, |language| {
9093 language.completions.as_ref()?.lsp.as_ref()
9094 })
9095 },
9096 write: |settings_content, value| {
9097 language_settings_field_mut(settings_content, value, |language, value| {
9098 language.completions.get_or_insert_default().lsp = value;
9099 })
9100 },
9101 }),
9102 metadata: None,
9103 files: USER | PROJECT,
9104 }),
9105 SettingsPageItem::SettingItem(SettingItem {
9106 title: "Fetch Timeout (milliseconds)",
9107 description: "When fetching LSP completions, determines how long to wait for a response of a particular server (set to 0 to wait indefinitely).",
9108 field: Box::new(SettingField {
9109 json_path: Some("languages.$(language).completions.lsp_fetch_timeout_ms"),
9110 pick: |settings_content| {
9111 language_settings_field(settings_content, |language| {
9112 language.completions.as_ref()?.lsp_fetch_timeout_ms.as_ref()
9113 })
9114 },
9115 write: |settings_content, value| {
9116 language_settings_field_mut(settings_content, value, |language, value| {
9117 language
9118 .completions
9119 .get_or_insert_default()
9120 .lsp_fetch_timeout_ms = value;
9121 })
9122 },
9123 }),
9124 metadata: None,
9125 files: USER | PROJECT,
9126 }),
9127 SettingsPageItem::SettingItem(SettingItem {
9128 title: "Insert Mode",
9129 description: "Controls how LSP completions are inserted.",
9130 field: Box::new(SettingField {
9131 json_path: Some("languages.$(language).completions.lsp_insert_mode"),
9132 pick: |settings_content| {
9133 language_settings_field(settings_content, |language| {
9134 language.completions.as_ref()?.lsp_insert_mode.as_ref()
9135 })
9136 },
9137 write: |settings_content, value| {
9138 language_settings_field_mut(settings_content, value, |language, value| {
9139 language.completions.get_or_insert_default().lsp_insert_mode = value;
9140 })
9141 },
9142 }),
9143 metadata: None,
9144 files: USER | PROJECT,
9145 }),
9146 ]
9147 }
9148
9149 fn debugger_section() -> [SettingsPageItem; 2] {
9150 [
9151 SettingsPageItem::SectionHeader("Debuggers"),
9152 SettingsPageItem::SettingItem(SettingItem {
9153 title: "Debuggers",
9154 description: "Preferred debuggers for this language.",
9155 field: Box::new(
9156 SettingField {
9157 json_path: Some("languages.$(language).debuggers"),
9158 pick: |settings_content| {
9159 language_settings_field(settings_content, |language| {
9160 language.debuggers.as_ref()
9161 })
9162 },
9163 write: |settings_content, value| {
9164 language_settings_field_mut(
9165 settings_content,
9166 value,
9167 |language, value| {
9168 language.debuggers = value;
9169 },
9170 )
9171 },
9172 }
9173 .unimplemented(),
9174 ),
9175 metadata: None,
9176 files: USER | PROJECT,
9177 }),
9178 ]
9179 }
9180
9181 fn prettier_section() -> [SettingsPageItem; 5] {
9182 [
9183 SettingsPageItem::SectionHeader("Prettier"),
9184 SettingsPageItem::SettingItem(SettingItem {
9185 title: "Allowed",
9186 description: "Enables or disables formatting with Prettier for a given language.",
9187 field: Box::new(SettingField {
9188 json_path: Some("languages.$(language).prettier.allowed"),
9189 pick: |settings_content| {
9190 language_settings_field(settings_content, |language| {
9191 language.prettier.as_ref()?.allowed.as_ref()
9192 })
9193 },
9194 write: |settings_content, value| {
9195 language_settings_field_mut(settings_content, value, |language, value| {
9196 language.prettier.get_or_insert_default().allowed = value;
9197 })
9198 },
9199 }),
9200 metadata: None,
9201 files: USER | PROJECT,
9202 }),
9203 SettingsPageItem::SettingItem(SettingItem {
9204 title: "Parser",
9205 description: "Forces Prettier integration to use a specific parser name when formatting files with the language.",
9206 field: Box::new(SettingField {
9207 json_path: Some("languages.$(language).prettier.parser"),
9208 pick: |settings_content| {
9209 language_settings_field(settings_content, |language| {
9210 language.prettier.as_ref()?.parser.as_ref()
9211 })
9212 },
9213 write: |settings_content, value| {
9214 language_settings_field_mut(settings_content, value, |language, value| {
9215 language.prettier.get_or_insert_default().parser = value;
9216 })
9217 },
9218 }),
9219 metadata: None,
9220 files: USER | PROJECT,
9221 }),
9222 SettingsPageItem::SettingItem(SettingItem {
9223 title: "Plugins",
9224 description: "Forces Prettier integration to use specific plugins when formatting files with the language.",
9225 field: Box::new(
9226 SettingField {
9227 json_path: Some("languages.$(language).prettier.plugins"),
9228 pick: |settings_content| {
9229 language_settings_field(settings_content, |language| {
9230 language.prettier.as_ref()?.plugins.as_ref()
9231 })
9232 },
9233 write: |settings_content, value| {
9234 language_settings_field_mut(
9235 settings_content,
9236 value,
9237 |language, value| {
9238 language.prettier.get_or_insert_default().plugins = value;
9239 },
9240 )
9241 },
9242 }
9243 .unimplemented(),
9244 ),
9245 metadata: None,
9246 files: USER | PROJECT,
9247 }),
9248 SettingsPageItem::SettingItem(SettingItem {
9249 title: "Options",
9250 description: "Default Prettier options, in the format as in package.json section for Prettier.",
9251 field: Box::new(
9252 SettingField {
9253 json_path: Some("languages.$(language).prettier.options"),
9254 pick: |settings_content| {
9255 language_settings_field(settings_content, |language| {
9256 language.prettier.as_ref()?.options.as_ref()
9257 })
9258 },
9259 write: |settings_content, value| {
9260 language_settings_field_mut(
9261 settings_content,
9262 value,
9263 |language, value| {
9264 language.prettier.get_or_insert_default().options = value;
9265 },
9266 )
9267 },
9268 }
9269 .unimplemented(),
9270 ),
9271 metadata: None,
9272 files: USER | PROJECT,
9273 }),
9274 ]
9275 }
9276
9277 concat_sections!(
9278 lsp_section(),
9279 lsp_completions_section(),
9280 debugger_section(),
9281 prettier_section(),
9282 )
9283}
9284
9285fn edit_prediction_language_settings_section() -> [SettingsPageItem; 4] {
9286 [
9287 SettingsPageItem::SectionHeader("Edit Predictions"),
9288 SettingsPageItem::SubPageLink(SubPageLink {
9289 title: "Configure Providers".into(),
9290 r#type: Default::default(),
9291 json_path: Some("edit_predictions.providers"),
9292 description: Some("Set up different edit prediction providers in complement to Zed's built-in Zeta model.".into()),
9293 in_json: false,
9294 files: USER,
9295 render: render_edit_prediction_setup_page
9296 }),
9297 SettingsPageItem::SettingItem(SettingItem {
9298 title: "Show Edit Predictions",
9299 description: "Controls whether edit predictions are shown immediately or manually.",
9300 field: Box::new(SettingField {
9301 json_path: Some("languages.$(language).show_edit_predictions"),
9302 pick: |settings_content| {
9303 language_settings_field(settings_content, |language| {
9304 language.show_edit_predictions.as_ref()
9305 })
9306 },
9307 write: |settings_content, value| {
9308 language_settings_field_mut(settings_content, value, |language, value| {
9309 language.show_edit_predictions = value;
9310 })
9311 },
9312 }),
9313 metadata: None,
9314 files: USER | PROJECT,
9315 }),
9316 SettingsPageItem::SettingItem(SettingItem {
9317 title: "Disable in Language Scopes",
9318 description: "Controls whether edit predictions are shown in the given language scopes.",
9319 field: Box::new(
9320 SettingField {
9321 json_path: Some("languages.$(language).edit_predictions_disabled_in"),
9322 pick: |settings_content| {
9323 language_settings_field(settings_content, |language| {
9324 language.edit_predictions_disabled_in.as_ref()
9325 })
9326 },
9327 write: |settings_content, value| {
9328 language_settings_field_mut(settings_content, value, |language, value| {
9329 language.edit_predictions_disabled_in = value;
9330 })
9331 },
9332 }
9333 .unimplemented(),
9334 ),
9335 metadata: None,
9336 files: USER | PROJECT,
9337 }),
9338 ]
9339}
9340
9341fn show_scrollbar_or_editor(
9342 settings_content: &SettingsContent,
9343 show: fn(&SettingsContent) -> Option<&settings::ShowScrollbar>,
9344) -> Option<&settings::ShowScrollbar> {
9345 show(settings_content).or(settings_content
9346 .editor
9347 .scrollbar
9348 .as_ref()
9349 .and_then(|scrollbar| scrollbar.show.as_ref()))
9350}
9351
9352fn dynamic_variants<T>() -> &'static [T::Discriminant]
9353where
9354 T: strum::IntoDiscriminant,
9355 T::Discriminant: strum::VariantArray,
9356{
9357 <<T as strum::IntoDiscriminant>::Discriminant as strum::VariantArray>::VARIANTS
9358}
9359
9360/// Updates the `vim_mode` setting, disabling `helix_mode` if present and
9361/// `vim_mode` is being enabled.
9362fn write_vim_mode(settings: &mut SettingsContent, value: Option<bool>) {
9363 if value == Some(true) && settings.helix_mode == Some(true) {
9364 settings.helix_mode = Some(false);
9365 }
9366 settings.vim_mode = value;
9367}
9368
9369/// Updates the `helix_mode` setting, disabling `vim_mode` if present and
9370/// `helix_mode` is being enabled.
9371fn write_helix_mode(settings: &mut SettingsContent, value: Option<bool>) {
9372 if value == Some(true) && settings.vim_mode == Some(true) {
9373 settings.vim_mode = Some(false);
9374 }
9375 settings.helix_mode = value;
9376}
9377
9378#[cfg(test)]
9379mod tests {
9380 use super::*;
9381
9382 #[test]
9383 fn test_write_vim_helix_mode() {
9384 // Enabling vim mode while `vim_mode` and `helix_mode` are not yet set
9385 // should only update the `vim_mode` setting.
9386 let mut settings = SettingsContent::default();
9387 write_vim_mode(&mut settings, Some(true));
9388 assert_eq!(settings.vim_mode, Some(true));
9389 assert_eq!(settings.helix_mode, None);
9390
9391 // Enabling helix mode while `vim_mode` and `helix_mode` are not yet set
9392 // should only update the `helix_mode` setting.
9393 let mut settings = SettingsContent::default();
9394 write_helix_mode(&mut settings, Some(true));
9395 assert_eq!(settings.helix_mode, Some(true));
9396 assert_eq!(settings.vim_mode, None);
9397
9398 // Disabling helix mode should only touch `helix_mode` setting when
9399 // `vim_mode` is not set.
9400 write_helix_mode(&mut settings, Some(false));
9401 assert_eq!(settings.helix_mode, Some(false));
9402 assert_eq!(settings.vim_mode, None);
9403
9404 // Enabling vim mode should update `vim_mode` but leave `helix_mode`
9405 // untouched.
9406 write_vim_mode(&mut settings, Some(true));
9407 assert_eq!(settings.vim_mode, Some(true));
9408 assert_eq!(settings.helix_mode, Some(false));
9409
9410 // Enabling helix mode should update `helix_mode` and disable
9411 // `vim_mode`.
9412 write_helix_mode(&mut settings, Some(true));
9413 assert_eq!(settings.helix_mode, Some(true));
9414 assert_eq!(settings.vim_mode, Some(false));
9415
9416 // Enabling vim mode should update `vim_mode` and disable
9417 // `helix_mode`.
9418 write_vim_mode(&mut settings, Some(true));
9419 assert_eq!(settings.vim_mode, Some(true));
9420 assert_eq!(settings.helix_mode, Some(false));
9421 }
9422}