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