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