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; 4] {
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 ]
4227 }
4228
4229 fn window_section() -> [SettingsPageItem; 3] {
4230 [
4231 SettingsPageItem::SectionHeader("Window"),
4232 // todo(settings_ui): Should we filter by platform.as_ref()?
4233 SettingsPageItem::SettingItem(SettingItem {
4234 title: "Use System Window Tabs",
4235 description: "(macOS only) whether to allow Windows to tab together.",
4236 field: Box::new(SettingField {
4237 json_path: Some("use_system_window_tabs"),
4238 pick: |settings_content| {
4239 settings_content.workspace.use_system_window_tabs.as_ref()
4240 },
4241 write: |settings_content, value| {
4242 settings_content.workspace.use_system_window_tabs = value;
4243 },
4244 }),
4245 metadata: None,
4246 files: USER,
4247 }),
4248 SettingsPageItem::SettingItem(SettingItem {
4249 title: "Window Decorations",
4250 description: "(Linux only) whether Zed or your compositor should draw window decorations.",
4251 field: Box::new(SettingField {
4252 json_path: Some("window_decorations"),
4253 pick: |settings_content| settings_content.workspace.window_decorations.as_ref(),
4254 write: |settings_content, value| {
4255 settings_content.workspace.window_decorations = value;
4256 },
4257 }),
4258 metadata: None,
4259 files: USER,
4260 }),
4261 ]
4262 }
4263
4264 fn pane_modifiers_section() -> [SettingsPageItem; 4] {
4265 [
4266 SettingsPageItem::SectionHeader("Pane Modifiers"),
4267 SettingsPageItem::SettingItem(SettingItem {
4268 title: "Inactive Opacity",
4269 description: "Opacity of inactive panels (0.0 - 1.0).",
4270 field: Box::new(SettingField {
4271 json_path: Some("active_pane_modifiers.inactive_opacity"),
4272 pick: |settings_content| {
4273 settings_content
4274 .workspace
4275 .active_pane_modifiers
4276 .as_ref()?
4277 .inactive_opacity
4278 .as_ref()
4279 },
4280 write: |settings_content, value| {
4281 settings_content
4282 .workspace
4283 .active_pane_modifiers
4284 .get_or_insert_default()
4285 .inactive_opacity = value;
4286 },
4287 }),
4288 metadata: None,
4289 files: USER,
4290 }),
4291 SettingsPageItem::SettingItem(SettingItem {
4292 title: "Border Size",
4293 description: "Size of the border surrounding the active pane.",
4294 field: Box::new(SettingField {
4295 json_path: Some("active_pane_modifiers.border_size"),
4296 pick: |settings_content| {
4297 settings_content
4298 .workspace
4299 .active_pane_modifiers
4300 .as_ref()?
4301 .border_size
4302 .as_ref()
4303 },
4304 write: |settings_content, value| {
4305 settings_content
4306 .workspace
4307 .active_pane_modifiers
4308 .get_or_insert_default()
4309 .border_size = value;
4310 },
4311 }),
4312 metadata: None,
4313 files: USER,
4314 }),
4315 SettingsPageItem::SettingItem(SettingItem {
4316 title: "Zoomed Padding",
4317 description: "Show padding for zoomed panes.",
4318 field: Box::new(SettingField {
4319 json_path: Some("zoomed_padding"),
4320 pick: |settings_content| settings_content.workspace.zoomed_padding.as_ref(),
4321 write: |settings_content, value| {
4322 settings_content.workspace.zoomed_padding = value;
4323 },
4324 }),
4325 metadata: None,
4326 files: USER,
4327 }),
4328 ]
4329 }
4330
4331 fn pane_split_direction_section() -> [SettingsPageItem; 3] {
4332 [
4333 SettingsPageItem::SectionHeader("Pane Split Direction"),
4334 SettingsPageItem::SettingItem(SettingItem {
4335 title: "Vertical Split Direction",
4336 description: "Direction to split vertically.",
4337 field: Box::new(SettingField {
4338 json_path: Some("pane_split_direction_vertical"),
4339 pick: |settings_content| {
4340 settings_content
4341 .workspace
4342 .pane_split_direction_vertical
4343 .as_ref()
4344 },
4345 write: |settings_content, value| {
4346 settings_content.workspace.pane_split_direction_vertical = value;
4347 },
4348 }),
4349 metadata: None,
4350 files: USER,
4351 }),
4352 SettingsPageItem::SettingItem(SettingItem {
4353 title: "Horizontal Split Direction",
4354 description: "Direction to split horizontally.",
4355 field: Box::new(SettingField {
4356 json_path: Some("pane_split_direction_horizontal"),
4357 pick: |settings_content| {
4358 settings_content
4359 .workspace
4360 .pane_split_direction_horizontal
4361 .as_ref()
4362 },
4363 write: |settings_content, value| {
4364 settings_content.workspace.pane_split_direction_horizontal = value;
4365 },
4366 }),
4367 metadata: None,
4368 files: USER,
4369 }),
4370 ]
4371 }
4372
4373 SettingsPage {
4374 title: "Window & Layout",
4375 items: concat_sections![
4376 status_bar_section(),
4377 title_bar_section(),
4378 tab_bar_section(),
4379 tab_settings_section(),
4380 preview_tabs_section(),
4381 layout_section(),
4382 window_section(),
4383 pane_modifiers_section(),
4384 pane_split_direction_section(),
4385 ],
4386 }
4387}
4388
4389fn panels_page() -> SettingsPage {
4390 fn project_panel_section() -> [SettingsPageItem; 24] {
4391 [
4392 SettingsPageItem::SectionHeader("Project Panel"),
4393 SettingsPageItem::SettingItem(SettingItem {
4394 title: "Project Panel Dock",
4395 description: "Where to dock the project panel.",
4396 field: Box::new(SettingField {
4397 json_path: Some("project_panel.dock"),
4398 pick: |settings_content| settings_content.project_panel.as_ref()?.dock.as_ref(),
4399 write: |settings_content, value| {
4400 settings_content.project_panel.get_or_insert_default().dock = value;
4401 },
4402 }),
4403 metadata: None,
4404 files: USER,
4405 }),
4406 SettingsPageItem::SettingItem(SettingItem {
4407 title: "Project Panel Default Width",
4408 description: "Default width of the project panel in pixels.",
4409 field: Box::new(SettingField {
4410 json_path: Some("project_panel.default_width"),
4411 pick: |settings_content| {
4412 settings_content
4413 .project_panel
4414 .as_ref()?
4415 .default_width
4416 .as_ref()
4417 },
4418 write: |settings_content, value| {
4419 settings_content
4420 .project_panel
4421 .get_or_insert_default()
4422 .default_width = value;
4423 },
4424 }),
4425 metadata: None,
4426 files: USER,
4427 }),
4428 SettingsPageItem::SettingItem(SettingItem {
4429 title: "Hide .gitignore",
4430 description: "Whether to hide the gitignore entries in the project panel.",
4431 field: Box::new(SettingField {
4432 json_path: Some("project_panel.hide_gitignore"),
4433 pick: |settings_content| {
4434 settings_content
4435 .project_panel
4436 .as_ref()?
4437 .hide_gitignore
4438 .as_ref()
4439 },
4440 write: |settings_content, value| {
4441 settings_content
4442 .project_panel
4443 .get_or_insert_default()
4444 .hide_gitignore = value;
4445 },
4446 }),
4447 metadata: None,
4448 files: USER,
4449 }),
4450 SettingsPageItem::SettingItem(SettingItem {
4451 title: "Entry Spacing",
4452 description: "Spacing between worktree entries in the project panel.",
4453 field: Box::new(SettingField {
4454 json_path: Some("project_panel.entry_spacing"),
4455 pick: |settings_content| {
4456 settings_content
4457 .project_panel
4458 .as_ref()?
4459 .entry_spacing
4460 .as_ref()
4461 },
4462 write: |settings_content, value| {
4463 settings_content
4464 .project_panel
4465 .get_or_insert_default()
4466 .entry_spacing = value;
4467 },
4468 }),
4469 metadata: None,
4470 files: USER,
4471 }),
4472 SettingsPageItem::SettingItem(SettingItem {
4473 title: "File Icons",
4474 description: "Show file icons in the project panel.",
4475 field: Box::new(SettingField {
4476 json_path: Some("project_panel.file_icons"),
4477 pick: |settings_content| {
4478 settings_content.project_panel.as_ref()?.file_icons.as_ref()
4479 },
4480 write: |settings_content, value| {
4481 settings_content
4482 .project_panel
4483 .get_or_insert_default()
4484 .file_icons = value;
4485 },
4486 }),
4487 metadata: None,
4488 files: USER,
4489 }),
4490 SettingsPageItem::SettingItem(SettingItem {
4491 title: "Folder Icons",
4492 description: "Whether to show folder icons or chevrons for directories in the project panel.",
4493 field: Box::new(SettingField {
4494 json_path: Some("project_panel.folder_icons"),
4495 pick: |settings_content| {
4496 settings_content
4497 .project_panel
4498 .as_ref()?
4499 .folder_icons
4500 .as_ref()
4501 },
4502 write: |settings_content, value| {
4503 settings_content
4504 .project_panel
4505 .get_or_insert_default()
4506 .folder_icons = value;
4507 },
4508 }),
4509 metadata: None,
4510 files: USER,
4511 }),
4512 SettingsPageItem::SettingItem(SettingItem {
4513 title: "Git Status",
4514 description: "Show the Git status in the project panel.",
4515 field: Box::new(SettingField {
4516 json_path: Some("project_panel.git_status"),
4517 pick: |settings_content| {
4518 settings_content.project_panel.as_ref()?.git_status.as_ref()
4519 },
4520 write: |settings_content, value| {
4521 settings_content
4522 .project_panel
4523 .get_or_insert_default()
4524 .git_status = value;
4525 },
4526 }),
4527 metadata: None,
4528 files: USER,
4529 }),
4530 SettingsPageItem::SettingItem(SettingItem {
4531 title: "Indent Size",
4532 description: "Amount of indentation for nested items.",
4533 field: Box::new(SettingField {
4534 json_path: Some("project_panel.indent_size"),
4535 pick: |settings_content| {
4536 settings_content
4537 .project_panel
4538 .as_ref()?
4539 .indent_size
4540 .as_ref()
4541 },
4542 write: |settings_content, value| {
4543 settings_content
4544 .project_panel
4545 .get_or_insert_default()
4546 .indent_size = value;
4547 },
4548 }),
4549 metadata: None,
4550 files: USER,
4551 }),
4552 SettingsPageItem::SettingItem(SettingItem {
4553 title: "Auto Reveal Entries",
4554 description: "Whether to reveal entries in the project panel automatically when a corresponding project entry becomes active.",
4555 field: Box::new(SettingField {
4556 json_path: Some("project_panel.auto_reveal_entries"),
4557 pick: |settings_content| {
4558 settings_content
4559 .project_panel
4560 .as_ref()?
4561 .auto_reveal_entries
4562 .as_ref()
4563 },
4564 write: |settings_content, value| {
4565 settings_content
4566 .project_panel
4567 .get_or_insert_default()
4568 .auto_reveal_entries = value;
4569 },
4570 }),
4571 metadata: None,
4572 files: USER,
4573 }),
4574 SettingsPageItem::SettingItem(SettingItem {
4575 title: "Starts Open",
4576 description: "Whether the project panel should open on startup.",
4577 field: Box::new(SettingField {
4578 json_path: Some("project_panel.starts_open"),
4579 pick: |settings_content| {
4580 settings_content
4581 .project_panel
4582 .as_ref()?
4583 .starts_open
4584 .as_ref()
4585 },
4586 write: |settings_content, value| {
4587 settings_content
4588 .project_panel
4589 .get_or_insert_default()
4590 .starts_open = value;
4591 },
4592 }),
4593 metadata: None,
4594 files: USER,
4595 }),
4596 SettingsPageItem::SettingItem(SettingItem {
4597 title: "Auto Fold Directories",
4598 description: "Whether to fold directories automatically and show compact folders when a directory has only one subdirectory inside.",
4599 field: Box::new(SettingField {
4600 json_path: Some("project_panel.auto_fold_dirs"),
4601 pick: |settings_content| {
4602 settings_content
4603 .project_panel
4604 .as_ref()?
4605 .auto_fold_dirs
4606 .as_ref()
4607 },
4608 write: |settings_content, value| {
4609 settings_content
4610 .project_panel
4611 .get_or_insert_default()
4612 .auto_fold_dirs = value;
4613 },
4614 }),
4615 metadata: None,
4616 files: USER,
4617 }),
4618 SettingsPageItem::SettingItem(SettingItem {
4619 title: "Bold Folder Labels",
4620 description: "Whether to show folder names with bold text in the project panel.",
4621 field: Box::new(SettingField {
4622 json_path: Some("project_panel.bold_folder_labels"),
4623 pick: |settings_content| {
4624 settings_content
4625 .project_panel
4626 .as_ref()?
4627 .bold_folder_labels
4628 .as_ref()
4629 },
4630 write: |settings_content, value| {
4631 settings_content
4632 .project_panel
4633 .get_or_insert_default()
4634 .bold_folder_labels = value;
4635 },
4636 }),
4637 metadata: None,
4638 files: USER,
4639 }),
4640 SettingsPageItem::SettingItem(SettingItem {
4641 title: "Show Scrollbar",
4642 description: "Show the scrollbar in the project panel.",
4643 field: Box::new(SettingField {
4644 json_path: Some("project_panel.scrollbar.show"),
4645 pick: |settings_content| {
4646 show_scrollbar_or_editor(settings_content, |settings_content| {
4647 settings_content
4648 .project_panel
4649 .as_ref()?
4650 .scrollbar
4651 .as_ref()?
4652 .show
4653 .as_ref()
4654 })
4655 },
4656 write: |settings_content, value| {
4657 settings_content
4658 .project_panel
4659 .get_or_insert_default()
4660 .scrollbar
4661 .get_or_insert_default()
4662 .show = value;
4663 },
4664 }),
4665 metadata: None,
4666 files: USER,
4667 }),
4668 SettingsPageItem::SettingItem(SettingItem {
4669 title: "Horizontal Scroll",
4670 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.",
4671 field: Box::new(SettingField {
4672 json_path: Some("project_panel.scrollbar.horizontal_scroll"),
4673 pick: |settings_content| {
4674 settings_content
4675 .project_panel
4676 .as_ref()?
4677 .scrollbar
4678 .as_ref()?
4679 .horizontal_scroll
4680 .as_ref()
4681 },
4682 write: |settings_content, value| {
4683 settings_content
4684 .project_panel
4685 .get_or_insert_default()
4686 .scrollbar
4687 .get_or_insert_default()
4688 .horizontal_scroll = value;
4689 },
4690 }),
4691 metadata: None,
4692 files: USER,
4693 }),
4694 SettingsPageItem::SettingItem(SettingItem {
4695 title: "Show Diagnostics",
4696 description: "Which files containing diagnostic errors/warnings to mark in the project panel.",
4697 field: Box::new(SettingField {
4698 json_path: Some("project_panel.show_diagnostics"),
4699 pick: |settings_content| {
4700 settings_content
4701 .project_panel
4702 .as_ref()?
4703 .show_diagnostics
4704 .as_ref()
4705 },
4706 write: |settings_content, value| {
4707 settings_content
4708 .project_panel
4709 .get_or_insert_default()
4710 .show_diagnostics = value;
4711 },
4712 }),
4713 metadata: None,
4714 files: USER,
4715 }),
4716 SettingsPageItem::SettingItem(SettingItem {
4717 title: "Diagnostic Badges",
4718 description: "Show error and warning count badges next to file names in the project panel.",
4719 field: Box::new(SettingField {
4720 json_path: Some("project_panel.diagnostic_badges"),
4721 pick: |settings_content| {
4722 settings_content
4723 .project_panel
4724 .as_ref()?
4725 .diagnostic_badges
4726 .as_ref()
4727 },
4728 write: |settings_content, value| {
4729 settings_content
4730 .project_panel
4731 .get_or_insert_default()
4732 .diagnostic_badges = value;
4733 },
4734 }),
4735 metadata: None,
4736 files: USER,
4737 }),
4738 SettingsPageItem::SettingItem(SettingItem {
4739 title: "Git Status Indicator",
4740 description: "Show a git status indicator next to file names in the project panel.",
4741 field: Box::new(SettingField {
4742 json_path: Some("project_panel.git_status_indicator"),
4743 pick: |settings_content| {
4744 settings_content
4745 .project_panel
4746 .as_ref()?
4747 .git_status_indicator
4748 .as_ref()
4749 },
4750 write: |settings_content, value| {
4751 settings_content
4752 .project_panel
4753 .get_or_insert_default()
4754 .git_status_indicator = value;
4755 },
4756 }),
4757 metadata: None,
4758 files: USER,
4759 }),
4760 SettingsPageItem::SettingItem(SettingItem {
4761 title: "Sticky Scroll",
4762 description: "Whether to stick parent directories at top of the project panel.",
4763 field: Box::new(SettingField {
4764 json_path: Some("project_panel.sticky_scroll"),
4765 pick: |settings_content| {
4766 settings_content
4767 .project_panel
4768 .as_ref()?
4769 .sticky_scroll
4770 .as_ref()
4771 },
4772 write: |settings_content, value| {
4773 settings_content
4774 .project_panel
4775 .get_or_insert_default()
4776 .sticky_scroll = value;
4777 },
4778 }),
4779 metadata: None,
4780 files: USER,
4781 }),
4782 SettingsPageItem::SettingItem(SettingItem {
4783 files: USER,
4784 title: "Show Indent Guides",
4785 description: "Show indent guides in the project panel.",
4786 field: Box::new(SettingField {
4787 json_path: Some("project_panel.indent_guides.show"),
4788 pick: |settings_content| {
4789 settings_content
4790 .project_panel
4791 .as_ref()?
4792 .indent_guides
4793 .as_ref()?
4794 .show
4795 .as_ref()
4796 },
4797 write: |settings_content, value| {
4798 settings_content
4799 .project_panel
4800 .get_or_insert_default()
4801 .indent_guides
4802 .get_or_insert_default()
4803 .show = value;
4804 },
4805 }),
4806 metadata: None,
4807 }),
4808 SettingsPageItem::SettingItem(SettingItem {
4809 title: "Drag and Drop",
4810 description: "Whether to enable drag-and-drop operations in the project panel.",
4811 field: Box::new(SettingField {
4812 json_path: Some("project_panel.drag_and_drop"),
4813 pick: |settings_content| {
4814 settings_content
4815 .project_panel
4816 .as_ref()?
4817 .drag_and_drop
4818 .as_ref()
4819 },
4820 write: |settings_content, value| {
4821 settings_content
4822 .project_panel
4823 .get_or_insert_default()
4824 .drag_and_drop = value;
4825 },
4826 }),
4827 metadata: None,
4828 files: USER,
4829 }),
4830 SettingsPageItem::SettingItem(SettingItem {
4831 title: "Hide Root",
4832 description: "Whether to hide the root entry when only one folder is open in the window.",
4833 field: Box::new(SettingField {
4834 json_path: Some("project_panel.hide_root"),
4835 pick: |settings_content| {
4836 settings_content.project_panel.as_ref()?.hide_root.as_ref()
4837 },
4838 write: |settings_content, value| {
4839 settings_content
4840 .project_panel
4841 .get_or_insert_default()
4842 .hide_root = value;
4843 },
4844 }),
4845 metadata: None,
4846 files: USER,
4847 }),
4848 SettingsPageItem::SettingItem(SettingItem {
4849 title: "Hide Hidden",
4850 description: "Whether to hide the hidden entries in the project panel.",
4851 field: Box::new(SettingField {
4852 json_path: Some("project_panel.hide_hidden"),
4853 pick: |settings_content| {
4854 settings_content
4855 .project_panel
4856 .as_ref()?
4857 .hide_hidden
4858 .as_ref()
4859 },
4860 write: |settings_content, value| {
4861 settings_content
4862 .project_panel
4863 .get_or_insert_default()
4864 .hide_hidden = value;
4865 },
4866 }),
4867 metadata: None,
4868 files: USER,
4869 }),
4870 SettingsPageItem::SettingItem(SettingItem {
4871 title: "Hidden Files",
4872 description: "Globs to match files that will be considered \"hidden\" and can be hidden from the project panel.",
4873 field: Box::new(
4874 SettingField {
4875 json_path: Some("worktree.hidden_files"),
4876 pick: |settings_content| {
4877 settings_content.project.worktree.hidden_files.as_ref()
4878 },
4879 write: |settings_content, value| {
4880 settings_content.project.worktree.hidden_files = value;
4881 },
4882 }
4883 .unimplemented(),
4884 ),
4885 metadata: None,
4886 files: USER,
4887 }),
4888 ]
4889 }
4890
4891 fn auto_open_files_section() -> [SettingsPageItem; 5] {
4892 [
4893 SettingsPageItem::SectionHeader("Auto Open Files"),
4894 SettingsPageItem::SettingItem(SettingItem {
4895 title: "On Create",
4896 description: "Whether to automatically open newly created files in the editor.",
4897 field: Box::new(SettingField {
4898 json_path: Some("project_panel.auto_open.on_create"),
4899 pick: |settings_content| {
4900 settings_content
4901 .project_panel
4902 .as_ref()?
4903 .auto_open
4904 .as_ref()?
4905 .on_create
4906 .as_ref()
4907 },
4908 write: |settings_content, value| {
4909 settings_content
4910 .project_panel
4911 .get_or_insert_default()
4912 .auto_open
4913 .get_or_insert_default()
4914 .on_create = value;
4915 },
4916 }),
4917 metadata: None,
4918 files: USER,
4919 }),
4920 SettingsPageItem::SettingItem(SettingItem {
4921 title: "On Paste",
4922 description: "Whether to automatically open files after pasting or duplicating them.",
4923 field: Box::new(SettingField {
4924 json_path: Some("project_panel.auto_open.on_paste"),
4925 pick: |settings_content| {
4926 settings_content
4927 .project_panel
4928 .as_ref()?
4929 .auto_open
4930 .as_ref()?
4931 .on_paste
4932 .as_ref()
4933 },
4934 write: |settings_content, value| {
4935 settings_content
4936 .project_panel
4937 .get_or_insert_default()
4938 .auto_open
4939 .get_or_insert_default()
4940 .on_paste = value;
4941 },
4942 }),
4943 metadata: None,
4944 files: USER,
4945 }),
4946 SettingsPageItem::SettingItem(SettingItem {
4947 title: "On Drop",
4948 description: "Whether to automatically open files dropped from external sources.",
4949 field: Box::new(SettingField {
4950 json_path: Some("project_panel.auto_open.on_drop"),
4951 pick: |settings_content| {
4952 settings_content
4953 .project_panel
4954 .as_ref()?
4955 .auto_open
4956 .as_ref()?
4957 .on_drop
4958 .as_ref()
4959 },
4960 write: |settings_content, value| {
4961 settings_content
4962 .project_panel
4963 .get_or_insert_default()
4964 .auto_open
4965 .get_or_insert_default()
4966 .on_drop = value;
4967 },
4968 }),
4969 metadata: None,
4970 files: USER,
4971 }),
4972 SettingsPageItem::SettingItem(SettingItem {
4973 title: "Sort Mode",
4974 description: "Sort order for entries in the project panel.",
4975 field: Box::new(SettingField {
4976 pick: |settings_content| {
4977 settings_content.project_panel.as_ref()?.sort_mode.as_ref()
4978 },
4979 write: |settings_content, value| {
4980 settings_content
4981 .project_panel
4982 .get_or_insert_default()
4983 .sort_mode = value;
4984 },
4985 json_path: Some("project_panel.sort_mode"),
4986 }),
4987 metadata: None,
4988 files: USER,
4989 }),
4990 ]
4991 }
4992
4993 fn terminal_panel_section() -> [SettingsPageItem; 4] {
4994 [
4995 SettingsPageItem::SectionHeader("Terminal Panel"),
4996 SettingsPageItem::SettingItem(SettingItem {
4997 title: "Terminal Dock",
4998 description: "Where to dock the terminal panel.",
4999 field: Box::new(SettingField {
5000 json_path: Some("terminal.dock"),
5001 pick: |settings_content| settings_content.terminal.as_ref()?.dock.as_ref(),
5002 write: |settings_content, value| {
5003 settings_content.terminal.get_or_insert_default().dock = value;
5004 },
5005 }),
5006 metadata: None,
5007 files: USER,
5008 }),
5009 SettingsPageItem::SettingItem(SettingItem {
5010 title: "Terminal Panel Flexible Sizing",
5011 description: "Whether the terminal panel should use flexible (proportional) sizing when docked to the left or right.",
5012 field: Box::new(SettingField {
5013 json_path: Some("terminal.flexible"),
5014 pick: |settings_content| settings_content.terminal.as_ref()?.flexible.as_ref(),
5015 write: |settings_content, value| {
5016 settings_content.terminal.get_or_insert_default().flexible = value;
5017 },
5018 }),
5019 metadata: None,
5020 files: USER,
5021 }),
5022 SettingsPageItem::SettingItem(SettingItem {
5023 title: "Show Count Badge",
5024 description: "Show a badge on the terminal panel icon with the count of open terminals.",
5025 field: Box::new(SettingField {
5026 json_path: Some("terminal.show_count_badge"),
5027 pick: |settings_content| {
5028 settings_content
5029 .terminal
5030 .as_ref()?
5031 .show_count_badge
5032 .as_ref()
5033 },
5034 write: |settings_content, value| {
5035 settings_content
5036 .terminal
5037 .get_or_insert_default()
5038 .show_count_badge = value;
5039 },
5040 }),
5041 metadata: None,
5042 files: USER,
5043 }),
5044 ]
5045 }
5046
5047 fn outline_panel_section() -> [SettingsPageItem; 11] {
5048 [
5049 SettingsPageItem::SectionHeader("Outline Panel"),
5050 SettingsPageItem::SettingItem(SettingItem {
5051 title: "Outline Panel Button",
5052 description: "Show the outline panel button in the status bar.",
5053 field: Box::new(SettingField {
5054 json_path: Some("outline_panel.button"),
5055 pick: |settings_content| {
5056 settings_content.outline_panel.as_ref()?.button.as_ref()
5057 },
5058 write: |settings_content, value| {
5059 settings_content
5060 .outline_panel
5061 .get_or_insert_default()
5062 .button = value;
5063 },
5064 }),
5065 metadata: None,
5066 files: USER,
5067 }),
5068 SettingsPageItem::SettingItem(SettingItem {
5069 title: "Outline Panel Dock",
5070 description: "Where to dock the outline panel.",
5071 field: Box::new(SettingField {
5072 json_path: Some("outline_panel.dock"),
5073 pick: |settings_content| settings_content.outline_panel.as_ref()?.dock.as_ref(),
5074 write: |settings_content, value| {
5075 settings_content.outline_panel.get_or_insert_default().dock = value;
5076 },
5077 }),
5078 metadata: None,
5079 files: USER,
5080 }),
5081 SettingsPageItem::SettingItem(SettingItem {
5082 title: "Outline Panel Default Width",
5083 description: "Default width of the outline panel in pixels.",
5084 field: Box::new(SettingField {
5085 json_path: Some("outline_panel.default_width"),
5086 pick: |settings_content| {
5087 settings_content
5088 .outline_panel
5089 .as_ref()?
5090 .default_width
5091 .as_ref()
5092 },
5093 write: |settings_content, value| {
5094 settings_content
5095 .outline_panel
5096 .get_or_insert_default()
5097 .default_width = value;
5098 },
5099 }),
5100 metadata: None,
5101 files: USER,
5102 }),
5103 SettingsPageItem::SettingItem(SettingItem {
5104 title: "File Icons",
5105 description: "Show file icons in the outline panel.",
5106 field: Box::new(SettingField {
5107 json_path: Some("outline_panel.file_icons"),
5108 pick: |settings_content| {
5109 settings_content.outline_panel.as_ref()?.file_icons.as_ref()
5110 },
5111 write: |settings_content, value| {
5112 settings_content
5113 .outline_panel
5114 .get_or_insert_default()
5115 .file_icons = value;
5116 },
5117 }),
5118 metadata: None,
5119 files: USER,
5120 }),
5121 SettingsPageItem::SettingItem(SettingItem {
5122 title: "Folder Icons",
5123 description: "Whether to show folder icons or chevrons for directories in the outline panel.",
5124 field: Box::new(SettingField {
5125 json_path: Some("outline_panel.folder_icons"),
5126 pick: |settings_content| {
5127 settings_content
5128 .outline_panel
5129 .as_ref()?
5130 .folder_icons
5131 .as_ref()
5132 },
5133 write: |settings_content, value| {
5134 settings_content
5135 .outline_panel
5136 .get_or_insert_default()
5137 .folder_icons = value;
5138 },
5139 }),
5140 metadata: None,
5141 files: USER,
5142 }),
5143 SettingsPageItem::SettingItem(SettingItem {
5144 title: "Git Status",
5145 description: "Show the Git status in the outline panel.",
5146 field: Box::new(SettingField {
5147 json_path: Some("outline_panel.git_status"),
5148 pick: |settings_content| {
5149 settings_content.outline_panel.as_ref()?.git_status.as_ref()
5150 },
5151 write: |settings_content, value| {
5152 settings_content
5153 .outline_panel
5154 .get_or_insert_default()
5155 .git_status = value;
5156 },
5157 }),
5158 metadata: None,
5159 files: USER,
5160 }),
5161 SettingsPageItem::SettingItem(SettingItem {
5162 title: "Indent Size",
5163 description: "Amount of indentation for nested items.",
5164 field: Box::new(SettingField {
5165 json_path: Some("outline_panel.indent_size"),
5166 pick: |settings_content| {
5167 settings_content
5168 .outline_panel
5169 .as_ref()?
5170 .indent_size
5171 .as_ref()
5172 },
5173 write: |settings_content, value| {
5174 settings_content
5175 .outline_panel
5176 .get_or_insert_default()
5177 .indent_size = value;
5178 },
5179 }),
5180 metadata: None,
5181 files: USER,
5182 }),
5183 SettingsPageItem::SettingItem(SettingItem {
5184 title: "Auto Reveal Entries",
5185 description: "Whether to reveal when a corresponding outline entry becomes active.",
5186 field: Box::new(SettingField {
5187 json_path: Some("outline_panel.auto_reveal_entries"),
5188 pick: |settings_content| {
5189 settings_content
5190 .outline_panel
5191 .as_ref()?
5192 .auto_reveal_entries
5193 .as_ref()
5194 },
5195 write: |settings_content, value| {
5196 settings_content
5197 .outline_panel
5198 .get_or_insert_default()
5199 .auto_reveal_entries = value;
5200 },
5201 }),
5202 metadata: None,
5203 files: USER,
5204 }),
5205 SettingsPageItem::SettingItem(SettingItem {
5206 title: "Auto Fold Directories",
5207 description: "Whether to fold directories automatically when a directory contains only one subdirectory.",
5208 field: Box::new(SettingField {
5209 json_path: Some("outline_panel.auto_fold_dirs"),
5210 pick: |settings_content| {
5211 settings_content
5212 .outline_panel
5213 .as_ref()?
5214 .auto_fold_dirs
5215 .as_ref()
5216 },
5217 write: |settings_content, value| {
5218 settings_content
5219 .outline_panel
5220 .get_or_insert_default()
5221 .auto_fold_dirs = value;
5222 },
5223 }),
5224 metadata: None,
5225 files: USER,
5226 }),
5227 SettingsPageItem::SettingItem(SettingItem {
5228 files: USER,
5229 title: "Show Indent Guides",
5230 description: "When to show indent guides in the outline panel.",
5231 field: Box::new(SettingField {
5232 json_path: Some("outline_panel.indent_guides.show"),
5233 pick: |settings_content| {
5234 settings_content
5235 .outline_panel
5236 .as_ref()?
5237 .indent_guides
5238 .as_ref()?
5239 .show
5240 .as_ref()
5241 },
5242 write: |settings_content, value| {
5243 settings_content
5244 .outline_panel
5245 .get_or_insert_default()
5246 .indent_guides
5247 .get_or_insert_default()
5248 .show = value;
5249 },
5250 }),
5251 metadata: None,
5252 }),
5253 ]
5254 }
5255
5256 fn git_panel_section() -> [SettingsPageItem; 14] {
5257 [
5258 SettingsPageItem::SectionHeader("Git Panel"),
5259 SettingsPageItem::SettingItem(SettingItem {
5260 title: "Git Panel Button",
5261 description: "Show the Git panel button in the status bar.",
5262 field: Box::new(SettingField {
5263 json_path: Some("git_panel.button"),
5264 pick: |settings_content| settings_content.git_panel.as_ref()?.button.as_ref(),
5265 write: |settings_content, value| {
5266 settings_content.git_panel.get_or_insert_default().button = value;
5267 },
5268 }),
5269 metadata: None,
5270 files: USER,
5271 }),
5272 SettingsPageItem::SettingItem(SettingItem {
5273 title: "Git Panel Dock",
5274 description: "Where to dock the Git panel.",
5275 field: Box::new(SettingField {
5276 json_path: Some("git_panel.dock"),
5277 pick: |settings_content| settings_content.git_panel.as_ref()?.dock.as_ref(),
5278 write: |settings_content, value| {
5279 settings_content.git_panel.get_or_insert_default().dock = value;
5280 },
5281 }),
5282 metadata: None,
5283 files: USER,
5284 }),
5285 SettingsPageItem::SettingItem(SettingItem {
5286 title: "Git Panel Default Width",
5287 description: "Default width of the Git panel in pixels.",
5288 field: Box::new(SettingField {
5289 json_path: Some("git_panel.default_width"),
5290 pick: |settings_content| {
5291 settings_content.git_panel.as_ref()?.default_width.as_ref()
5292 },
5293 write: |settings_content, value| {
5294 settings_content
5295 .git_panel
5296 .get_or_insert_default()
5297 .default_width = value;
5298 },
5299 }),
5300 metadata: None,
5301 files: USER,
5302 }),
5303 SettingsPageItem::SettingItem(SettingItem {
5304 title: "Git Panel Status Style",
5305 description: "How entry statuses are displayed.",
5306 field: Box::new(SettingField {
5307 json_path: Some("git_panel.status_style"),
5308 pick: |settings_content| {
5309 settings_content.git_panel.as_ref()?.status_style.as_ref()
5310 },
5311 write: |settings_content, value| {
5312 settings_content
5313 .git_panel
5314 .get_or_insert_default()
5315 .status_style = value;
5316 },
5317 }),
5318 metadata: None,
5319 files: USER,
5320 }),
5321 SettingsPageItem::SettingItem(SettingItem {
5322 title: "Fallback Branch Name",
5323 description: "Default branch name will be when init.defaultbranch is not set in Git.",
5324 field: Box::new(SettingField {
5325 json_path: Some("git_panel.fallback_branch_name"),
5326 pick: |settings_content| {
5327 settings_content
5328 .git_panel
5329 .as_ref()?
5330 .fallback_branch_name
5331 .as_ref()
5332 },
5333 write: |settings_content, value| {
5334 settings_content
5335 .git_panel
5336 .get_or_insert_default()
5337 .fallback_branch_name = value;
5338 },
5339 }),
5340 metadata: None,
5341 files: USER,
5342 }),
5343 SettingsPageItem::SettingItem(SettingItem {
5344 title: "Sort By Path",
5345 description: "Enable to sort entries in the panel by path, disable to sort by status.",
5346 field: Box::new(SettingField {
5347 json_path: Some("git_panel.sort_by_path"),
5348 pick: |settings_content| {
5349 settings_content.git_panel.as_ref()?.sort_by_path.as_ref()
5350 },
5351 write: |settings_content, value| {
5352 settings_content
5353 .git_panel
5354 .get_or_insert_default()
5355 .sort_by_path = value;
5356 },
5357 }),
5358 metadata: None,
5359 files: USER,
5360 }),
5361 SettingsPageItem::SettingItem(SettingItem {
5362 title: "Collapse Untracked Diff",
5363 description: "Whether to collapse untracked files in the diff panel.",
5364 field: Box::new(SettingField {
5365 json_path: Some("git_panel.collapse_untracked_diff"),
5366 pick: |settings_content| {
5367 settings_content
5368 .git_panel
5369 .as_ref()?
5370 .collapse_untracked_diff
5371 .as_ref()
5372 },
5373 write: |settings_content, value| {
5374 settings_content
5375 .git_panel
5376 .get_or_insert_default()
5377 .collapse_untracked_diff = value;
5378 },
5379 }),
5380 metadata: None,
5381 files: USER,
5382 }),
5383 SettingsPageItem::SettingItem(SettingItem {
5384 title: "Tree View",
5385 description: "Enable to show entries in tree view list, disable to show in flat view list.",
5386 field: Box::new(SettingField {
5387 json_path: Some("git_panel.tree_view"),
5388 pick: |settings_content| {
5389 settings_content.git_panel.as_ref()?.tree_view.as_ref()
5390 },
5391 write: |settings_content, value| {
5392 settings_content.git_panel.get_or_insert_default().tree_view = value;
5393 },
5394 }),
5395 metadata: None,
5396 files: USER,
5397 }),
5398 SettingsPageItem::SettingItem(SettingItem {
5399 title: "File Icons",
5400 description: "Show file icons next to the Git status icon.",
5401 field: Box::new(SettingField {
5402 json_path: Some("git_panel.file_icons"),
5403 pick: |settings_content| {
5404 settings_content.git_panel.as_ref()?.file_icons.as_ref()
5405 },
5406 write: |settings_content, value| {
5407 settings_content
5408 .git_panel
5409 .get_or_insert_default()
5410 .file_icons = value;
5411 },
5412 }),
5413 metadata: None,
5414 files: USER,
5415 }),
5416 SettingsPageItem::SettingItem(SettingItem {
5417 title: "Folder Icons",
5418 description: "Whether to show folder icons or chevrons for directories in the git panel.",
5419 field: Box::new(SettingField {
5420 json_path: Some("git_panel.folder_icons"),
5421 pick: |settings_content| {
5422 settings_content.git_panel.as_ref()?.folder_icons.as_ref()
5423 },
5424 write: |settings_content, value| {
5425 settings_content
5426 .git_panel
5427 .get_or_insert_default()
5428 .folder_icons = value;
5429 },
5430 }),
5431 metadata: None,
5432 files: USER,
5433 }),
5434 SettingsPageItem::SettingItem(SettingItem {
5435 title: "Diff Stats",
5436 description: "Whether to show the addition/deletion change count next to each file in the Git panel.",
5437 field: Box::new(SettingField {
5438 json_path: Some("git_panel.diff_stats"),
5439 pick: |settings_content| {
5440 settings_content.git_panel.as_ref()?.diff_stats.as_ref()
5441 },
5442 write: |settings_content, value| {
5443 settings_content
5444 .git_panel
5445 .get_or_insert_default()
5446 .diff_stats = value;
5447 },
5448 }),
5449 metadata: None,
5450 files: USER,
5451 }),
5452 SettingsPageItem::SettingItem(SettingItem {
5453 title: "Show Count Badge",
5454 description: "Whether to show a badge on the git panel icon with the count of uncommitted changes.",
5455 field: Box::new(SettingField {
5456 json_path: Some("git_panel.show_count_badge"),
5457 pick: |settings_content| {
5458 settings_content
5459 .git_panel
5460 .as_ref()?
5461 .show_count_badge
5462 .as_ref()
5463 },
5464 write: |settings_content, value| {
5465 settings_content
5466 .git_panel
5467 .get_or_insert_default()
5468 .show_count_badge = value;
5469 },
5470 }),
5471 metadata: None,
5472 files: USER,
5473 }),
5474 SettingsPageItem::SettingItem(SettingItem {
5475 title: "Scroll Bar",
5476 description: "How and when the scrollbar should be displayed.",
5477 field: Box::new(SettingField {
5478 json_path: Some("git_panel.scrollbar.show"),
5479 pick: |settings_content| {
5480 show_scrollbar_or_editor(settings_content, |settings_content| {
5481 settings_content
5482 .git_panel
5483 .as_ref()?
5484 .scrollbar
5485 .as_ref()?
5486 .show
5487 .as_ref()
5488 })
5489 },
5490 write: |settings_content, value| {
5491 settings_content
5492 .git_panel
5493 .get_or_insert_default()
5494 .scrollbar
5495 .get_or_insert_default()
5496 .show = value;
5497 },
5498 }),
5499 metadata: None,
5500 files: USER,
5501 }),
5502 ]
5503 }
5504
5505 fn debugger_panel_section() -> [SettingsPageItem; 2] {
5506 [
5507 SettingsPageItem::SectionHeader("Debugger Panel"),
5508 SettingsPageItem::SettingItem(SettingItem {
5509 title: "Debugger Panel Dock",
5510 description: "The dock position of the debug panel.",
5511 field: Box::new(SettingField {
5512 json_path: Some("debugger.dock"),
5513 pick: |settings_content| settings_content.debugger.as_ref()?.dock.as_ref(),
5514 write: |settings_content, value| {
5515 settings_content.debugger.get_or_insert_default().dock = value;
5516 },
5517 }),
5518 metadata: None,
5519 files: USER,
5520 }),
5521 ]
5522 }
5523
5524 fn notification_panel_section() -> [SettingsPageItem; 5] {
5525 [
5526 SettingsPageItem::SectionHeader("Notification Panel"),
5527 SettingsPageItem::SettingItem(SettingItem {
5528 title: "Notification Panel Button",
5529 description: "Show the notification panel button in the status bar.",
5530 field: Box::new(SettingField {
5531 json_path: Some("notification_panel.button"),
5532 pick: |settings_content| {
5533 settings_content
5534 .notification_panel
5535 .as_ref()?
5536 .button
5537 .as_ref()
5538 },
5539 write: |settings_content, value| {
5540 settings_content
5541 .notification_panel
5542 .get_or_insert_default()
5543 .button = value;
5544 },
5545 }),
5546 metadata: None,
5547 files: USER,
5548 }),
5549 SettingsPageItem::SettingItem(SettingItem {
5550 title: "Notification Panel Dock",
5551 description: "Where to dock the notification panel.",
5552 field: Box::new(SettingField {
5553 json_path: Some("notification_panel.dock"),
5554 pick: |settings_content| {
5555 settings_content.notification_panel.as_ref()?.dock.as_ref()
5556 },
5557 write: |settings_content, value| {
5558 settings_content
5559 .notification_panel
5560 .get_or_insert_default()
5561 .dock = value;
5562 },
5563 }),
5564 metadata: None,
5565 files: USER,
5566 }),
5567 SettingsPageItem::SettingItem(SettingItem {
5568 title: "Notification Panel Default Width",
5569 description: "Default width of the notification panel in pixels.",
5570 field: Box::new(SettingField {
5571 json_path: Some("notification_panel.default_width"),
5572 pick: |settings_content| {
5573 settings_content
5574 .notification_panel
5575 .as_ref()?
5576 .default_width
5577 .as_ref()
5578 },
5579 write: |settings_content, value| {
5580 settings_content
5581 .notification_panel
5582 .get_or_insert_default()
5583 .default_width = value;
5584 },
5585 }),
5586 metadata: None,
5587 files: USER,
5588 }),
5589 SettingsPageItem::SettingItem(SettingItem {
5590 title: "Show Count Badge",
5591 description: "Show a badge on the notification panel icon with the count of unread notifications.",
5592 field: Box::new(SettingField {
5593 json_path: Some("notification_panel.show_count_badge"),
5594 pick: |settings_content| {
5595 settings_content
5596 .notification_panel
5597 .as_ref()?
5598 .show_count_badge
5599 .as_ref()
5600 },
5601 write: |settings_content, value| {
5602 settings_content
5603 .notification_panel
5604 .get_or_insert_default()
5605 .show_count_badge = value;
5606 },
5607 }),
5608 metadata: None,
5609 files: USER,
5610 }),
5611 ]
5612 }
5613
5614 fn collaboration_panel_section() -> [SettingsPageItem; 4] {
5615 [
5616 SettingsPageItem::SectionHeader("Collaboration Panel"),
5617 SettingsPageItem::SettingItem(SettingItem {
5618 title: "Collaboration Panel Button",
5619 description: "Show the collaboration panel button in the status bar.",
5620 field: Box::new(SettingField {
5621 json_path: Some("collaboration_panel.button"),
5622 pick: |settings_content| {
5623 settings_content
5624 .collaboration_panel
5625 .as_ref()?
5626 .button
5627 .as_ref()
5628 },
5629 write: |settings_content, value| {
5630 settings_content
5631 .collaboration_panel
5632 .get_or_insert_default()
5633 .button = value;
5634 },
5635 }),
5636 metadata: None,
5637 files: USER,
5638 }),
5639 SettingsPageItem::SettingItem(SettingItem {
5640 title: "Collaboration Panel Dock",
5641 description: "Where to dock the collaboration panel.",
5642 field: Box::new(SettingField {
5643 json_path: Some("collaboration_panel.dock"),
5644 pick: |settings_content| {
5645 settings_content.collaboration_panel.as_ref()?.dock.as_ref()
5646 },
5647 write: |settings_content, value| {
5648 settings_content
5649 .collaboration_panel
5650 .get_or_insert_default()
5651 .dock = value;
5652 },
5653 }),
5654 metadata: None,
5655 files: USER,
5656 }),
5657 SettingsPageItem::SettingItem(SettingItem {
5658 title: "Collaboration Panel Default Width",
5659 description: "Default width of the collaboration panel in pixels.",
5660 field: Box::new(SettingField {
5661 json_path: Some("collaboration_panel.dock"),
5662 pick: |settings_content| {
5663 settings_content
5664 .collaboration_panel
5665 .as_ref()?
5666 .default_width
5667 .as_ref()
5668 },
5669 write: |settings_content, value| {
5670 settings_content
5671 .collaboration_panel
5672 .get_or_insert_default()
5673 .default_width = value;
5674 },
5675 }),
5676 metadata: None,
5677 files: USER,
5678 }),
5679 ]
5680 }
5681
5682 fn agent_panel_section() -> [SettingsPageItem; 6] {
5683 [
5684 SettingsPageItem::SectionHeader("Agent Panel"),
5685 SettingsPageItem::SettingItem(SettingItem {
5686 title: "Agent Panel Button",
5687 description: "Whether to show the agent panel button in the status bar.",
5688 field: Box::new(SettingField {
5689 json_path: Some("agent.button"),
5690 pick: |settings_content| settings_content.agent.as_ref()?.button.as_ref(),
5691 write: |settings_content, value| {
5692 settings_content.agent.get_or_insert_default().button = value;
5693 },
5694 }),
5695 metadata: None,
5696 files: USER,
5697 }),
5698 SettingsPageItem::SettingItem(SettingItem {
5699 title: "Agent Panel Dock",
5700 description: "Where to dock the agent panel.",
5701 field: Box::new(SettingField {
5702 json_path: Some("agent.dock"),
5703 pick: |settings_content| settings_content.agent.as_ref()?.dock.as_ref(),
5704 write: |settings_content, value| {
5705 settings_content.agent.get_or_insert_default().dock = value;
5706 },
5707 }),
5708 metadata: None,
5709 files: USER,
5710 }),
5711 SettingsPageItem::SettingItem(SettingItem {
5712 title: "Agent Panel Flexible Sizing",
5713 description: "Whether the agent panel should use flexible (proportional) sizing when docked to the left or right.",
5714 field: Box::new(SettingField {
5715 json_path: Some("agent.flexible"),
5716 pick: |settings_content| settings_content.agent.as_ref()?.flexible.as_ref(),
5717 write: |settings_content, value| {
5718 settings_content.agent.get_or_insert_default().flexible = value;
5719 },
5720 }),
5721 metadata: None,
5722 files: USER,
5723 }),
5724 SettingsPageItem::SettingItem(SettingItem {
5725 title: "Agent Panel Default Width",
5726 description: "Default width when the agent panel is docked to the left or right.",
5727 field: Box::new(SettingField {
5728 json_path: Some("agent.default_width"),
5729 pick: |settings_content| {
5730 settings_content.agent.as_ref()?.default_width.as_ref()
5731 },
5732 write: |settings_content, value| {
5733 settings_content.agent.get_or_insert_default().default_width = value;
5734 },
5735 }),
5736 metadata: None,
5737 files: USER,
5738 }),
5739 SettingsPageItem::SettingItem(SettingItem {
5740 title: "Agent Panel Default Height",
5741 description: "Default height when the agent panel is docked to the bottom.",
5742 field: Box::new(SettingField {
5743 json_path: Some("agent.default_height"),
5744 pick: |settings_content| {
5745 settings_content.agent.as_ref()?.default_height.as_ref()
5746 },
5747 write: |settings_content, value| {
5748 settings_content
5749 .agent
5750 .get_or_insert_default()
5751 .default_height = value;
5752 },
5753 }),
5754 metadata: None,
5755 files: USER,
5756 }),
5757 ]
5758 }
5759
5760 SettingsPage {
5761 title: "Panels",
5762 items: concat_sections![
5763 project_panel_section(),
5764 auto_open_files_section(),
5765 terminal_panel_section(),
5766 outline_panel_section(),
5767 git_panel_section(),
5768 debugger_panel_section(),
5769 notification_panel_section(),
5770 collaboration_panel_section(),
5771 agent_panel_section(),
5772 ],
5773 }
5774}
5775
5776fn debugger_page() -> SettingsPage {
5777 fn general_section() -> [SettingsPageItem; 6] {
5778 [
5779 SettingsPageItem::SectionHeader("General"),
5780 SettingsPageItem::SettingItem(SettingItem {
5781 title: "Stepping Granularity",
5782 description: "Determines the stepping granularity for debug operations.",
5783 field: Box::new(SettingField {
5784 json_path: Some("debugger.stepping_granularity"),
5785 pick: |settings_content| {
5786 settings_content
5787 .debugger
5788 .as_ref()?
5789 .stepping_granularity
5790 .as_ref()
5791 },
5792 write: |settings_content, value| {
5793 settings_content
5794 .debugger
5795 .get_or_insert_default()
5796 .stepping_granularity = value;
5797 },
5798 }),
5799 metadata: None,
5800 files: USER,
5801 }),
5802 SettingsPageItem::SettingItem(SettingItem {
5803 title: "Save Breakpoints",
5804 description: "Whether breakpoints should be reused across Zed sessions.",
5805 field: Box::new(SettingField {
5806 json_path: Some("debugger.save_breakpoints"),
5807 pick: |settings_content| {
5808 settings_content
5809 .debugger
5810 .as_ref()?
5811 .save_breakpoints
5812 .as_ref()
5813 },
5814 write: |settings_content, value| {
5815 settings_content
5816 .debugger
5817 .get_or_insert_default()
5818 .save_breakpoints = value;
5819 },
5820 }),
5821 metadata: None,
5822 files: USER,
5823 }),
5824 SettingsPageItem::SettingItem(SettingItem {
5825 title: "Timeout",
5826 description: "Time in milliseconds until timeout error when connecting to a TCP debug adapter.",
5827 field: Box::new(SettingField {
5828 json_path: Some("debugger.timeout"),
5829 pick: |settings_content| settings_content.debugger.as_ref()?.timeout.as_ref(),
5830 write: |settings_content, value| {
5831 settings_content.debugger.get_or_insert_default().timeout = value;
5832 },
5833 }),
5834 metadata: None,
5835 files: USER,
5836 }),
5837 SettingsPageItem::SettingItem(SettingItem {
5838 title: "Log DAP Communications",
5839 description: "Whether to log messages between active debug adapters and Zed.",
5840 field: Box::new(SettingField {
5841 json_path: Some("debugger.log_dap_communications"),
5842 pick: |settings_content| {
5843 settings_content
5844 .debugger
5845 .as_ref()?
5846 .log_dap_communications
5847 .as_ref()
5848 },
5849 write: |settings_content, value| {
5850 settings_content
5851 .debugger
5852 .get_or_insert_default()
5853 .log_dap_communications = value;
5854 },
5855 }),
5856 metadata: None,
5857 files: USER,
5858 }),
5859 SettingsPageItem::SettingItem(SettingItem {
5860 title: "Format DAP Log Messages",
5861 description: "Whether to format DAP messages when adding them to debug adapter logger.",
5862 field: Box::new(SettingField {
5863 json_path: Some("debugger.format_dap_log_messages"),
5864 pick: |settings_content| {
5865 settings_content
5866 .debugger
5867 .as_ref()?
5868 .format_dap_log_messages
5869 .as_ref()
5870 },
5871 write: |settings_content, value| {
5872 settings_content
5873 .debugger
5874 .get_or_insert_default()
5875 .format_dap_log_messages = value;
5876 },
5877 }),
5878 metadata: None,
5879 files: USER,
5880 }),
5881 ]
5882 }
5883
5884 SettingsPage {
5885 title: "Debugger",
5886 items: concat_sections![general_section()],
5887 }
5888}
5889
5890fn terminal_page() -> SettingsPage {
5891 fn environment_section() -> [SettingsPageItem; 5] {
5892 [
5893 SettingsPageItem::SectionHeader("Environment"),
5894 SettingsPageItem::DynamicItem(DynamicItem {
5895 discriminant: SettingItem {
5896 files: USER | PROJECT,
5897 title: "Shell",
5898 description: "What shell to use when opening a terminal.",
5899 field: Box::new(SettingField {
5900 json_path: Some("terminal.shell$"),
5901 pick: |settings_content| {
5902 Some(&dynamic_variants::<settings::Shell>()[
5903 settings_content
5904 .terminal
5905 .as_ref()?
5906 .project
5907 .shell
5908 .as_ref()?
5909 .discriminant() as usize
5910 ])
5911 },
5912 write: |settings_content, value| {
5913 let Some(value) = value else {
5914 if let Some(terminal) = settings_content.terminal.as_mut() {
5915 terminal.project.shell = None;
5916 }
5917 return;
5918 };
5919 let settings_value = settings_content
5920 .terminal
5921 .get_or_insert_default()
5922 .project
5923 .shell
5924 .get_or_insert_with(|| settings::Shell::default());
5925 let default_shell = if cfg!(target_os = "windows") {
5926 "powershell.exe"
5927 } else {
5928 "sh"
5929 };
5930 *settings_value = match value {
5931 settings::ShellDiscriminants::System => settings::Shell::System,
5932 settings::ShellDiscriminants::Program => {
5933 let program = match settings_value {
5934 settings::Shell::Program(program) => program.clone(),
5935 settings::Shell::WithArguments { program, .. } => program.clone(),
5936 _ => String::from(default_shell),
5937 };
5938 settings::Shell::Program(program)
5939 }
5940 settings::ShellDiscriminants::WithArguments => {
5941 let (program, args, title_override) = match settings_value {
5942 settings::Shell::Program(program) => (program.clone(), vec![], None),
5943 settings::Shell::WithArguments {
5944 program,
5945 args,
5946 title_override,
5947 } => (program.clone(), args.clone(), title_override.clone()),
5948 _ => (String::from(default_shell), vec![], None),
5949 };
5950 settings::Shell::WithArguments {
5951 program,
5952 args,
5953 title_override,
5954 }
5955 }
5956 };
5957 },
5958 }),
5959 metadata: None,
5960 },
5961 pick_discriminant: |settings_content| {
5962 Some(
5963 settings_content
5964 .terminal
5965 .as_ref()?
5966 .project
5967 .shell
5968 .as_ref()?
5969 .discriminant() as usize,
5970 )
5971 },
5972 fields: dynamic_variants::<settings::Shell>()
5973 .into_iter()
5974 .map(|variant| match variant {
5975 settings::ShellDiscriminants::System => vec![],
5976 settings::ShellDiscriminants::Program => vec![SettingItem {
5977 files: USER | PROJECT,
5978 title: "Program",
5979 description: "The shell program to use.",
5980 field: Box::new(SettingField {
5981 json_path: Some("terminal.shell"),
5982 pick: |settings_content| match settings_content.terminal.as_ref()?.project.shell.as_ref()
5983 {
5984 Some(settings::Shell::Program(program)) => Some(program),
5985 _ => None,
5986 },
5987 write: |settings_content, value| {
5988 let Some(value) = value else {
5989 return;
5990 };
5991 match settings_content
5992 .terminal
5993 .get_or_insert_default()
5994 .project
5995 .shell
5996 .as_mut()
5997 {
5998 Some(settings::Shell::Program(program)) => *program = value,
5999 _ => return,
6000 }
6001 },
6002 }),
6003 metadata: None,
6004 }],
6005 settings::ShellDiscriminants::WithArguments => vec![
6006 SettingItem {
6007 files: USER | PROJECT,
6008 title: "Program",
6009 description: "The shell program to run.",
6010 field: Box::new(SettingField {
6011 json_path: Some("terminal.shell.program"),
6012 pick: |settings_content| {
6013 match settings_content.terminal.as_ref()?.project.shell.as_ref() {
6014 Some(settings::Shell::WithArguments { program, .. }) => Some(program),
6015 _ => None,
6016 }
6017 },
6018 write: |settings_content, value| {
6019 let Some(value) = value else {
6020 return;
6021 };
6022 match settings_content
6023 .terminal
6024 .get_or_insert_default()
6025 .project
6026 .shell
6027 .as_mut()
6028 {
6029 Some(settings::Shell::WithArguments { program, .. }) => {
6030 *program = value
6031 }
6032 _ => return,
6033 }
6034 },
6035 }),
6036 metadata: None,
6037 },
6038 SettingItem {
6039 files: USER | PROJECT,
6040 title: "Arguments",
6041 description: "The arguments to pass to the shell program.",
6042 field: Box::new(
6043 SettingField {
6044 json_path: Some("terminal.shell.args"),
6045 pick: |settings_content| {
6046 match settings_content.terminal.as_ref()?.project.shell.as_ref() {
6047 Some(settings::Shell::WithArguments { args, .. }) => Some(args),
6048 _ => None,
6049 }
6050 },
6051 write: |settings_content, value| {
6052 let Some(value) = value else {
6053 return;
6054 };
6055 match settings_content
6056 .terminal
6057 .get_or_insert_default()
6058 .project
6059 .shell
6060 .as_mut()
6061 {
6062 Some(settings::Shell::WithArguments { args, .. }) => *args = value,
6063 _ => return,
6064 }
6065 },
6066 }
6067 .unimplemented(),
6068 ),
6069 metadata: None,
6070 },
6071 SettingItem {
6072 files: USER | PROJECT,
6073 title: "Title Override",
6074 description: "An optional string to override the title of the terminal tab.",
6075 field: Box::new(SettingField {
6076 json_path: Some("terminal.shell.title_override"),
6077 pick: |settings_content| {
6078 match settings_content.terminal.as_ref()?.project.shell.as_ref() {
6079 Some(settings::Shell::WithArguments { title_override, .. }) => {
6080 title_override.as_ref().or(DEFAULT_EMPTY_STRING)
6081 }
6082 _ => None,
6083 }
6084 },
6085 write: |settings_content, value| {
6086 match settings_content
6087 .terminal
6088 .get_or_insert_default()
6089 .project
6090 .shell
6091 .as_mut()
6092 {
6093 Some(settings::Shell::WithArguments { title_override, .. }) => {
6094 *title_override = value.filter(|s| !s.is_empty())
6095 }
6096 _ => return,
6097 }
6098 },
6099 }),
6100 metadata: None,
6101 },
6102 ],
6103 })
6104 .collect(),
6105 }),
6106 SettingsPageItem::DynamicItem(DynamicItem {
6107 discriminant: SettingItem {
6108 files: USER | PROJECT,
6109 title: "Working Directory",
6110 description: "What working directory to use when launching the terminal.",
6111 field: Box::new(SettingField {
6112 json_path: Some("terminal.working_directory$"),
6113 pick: |settings_content| {
6114 Some(&dynamic_variants::<settings::WorkingDirectory>()[
6115 settings_content
6116 .terminal
6117 .as_ref()?
6118 .project
6119 .working_directory
6120 .as_ref()?
6121 .discriminant() as usize
6122 ])
6123 },
6124 write: |settings_content, value| {
6125 let Some(value) = value else {
6126 if let Some(terminal) = settings_content.terminal.as_mut() {
6127 terminal.project.working_directory = None;
6128 }
6129 return;
6130 };
6131 let settings_value = settings_content
6132 .terminal
6133 .get_or_insert_default()
6134 .project
6135 .working_directory
6136 .get_or_insert_with(|| settings::WorkingDirectory::CurrentProjectDirectory);
6137 *settings_value = match value {
6138 settings::WorkingDirectoryDiscriminants::CurrentFileDirectory => {
6139 settings::WorkingDirectory::CurrentFileDirectory
6140 },
6141 settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => {
6142 settings::WorkingDirectory::CurrentProjectDirectory
6143 }
6144 settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => {
6145 settings::WorkingDirectory::FirstProjectDirectory
6146 }
6147 settings::WorkingDirectoryDiscriminants::AlwaysHome => {
6148 settings::WorkingDirectory::AlwaysHome
6149 }
6150 settings::WorkingDirectoryDiscriminants::Always => {
6151 let directory = match settings_value {
6152 settings::WorkingDirectory::Always { .. } => return,
6153 _ => String::new(),
6154 };
6155 settings::WorkingDirectory::Always { directory }
6156 }
6157 };
6158 },
6159 }),
6160 metadata: None,
6161 },
6162 pick_discriminant: |settings_content| {
6163 Some(
6164 settings_content
6165 .terminal
6166 .as_ref()?
6167 .project
6168 .working_directory
6169 .as_ref()?
6170 .discriminant() as usize,
6171 )
6172 },
6173 fields: dynamic_variants::<settings::WorkingDirectory>()
6174 .into_iter()
6175 .map(|variant| match variant {
6176 settings::WorkingDirectoryDiscriminants::CurrentFileDirectory => vec![],
6177 settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => vec![],
6178 settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => vec![],
6179 settings::WorkingDirectoryDiscriminants::AlwaysHome => vec![],
6180 settings::WorkingDirectoryDiscriminants::Always => vec![SettingItem {
6181 files: USER | PROJECT,
6182 title: "Directory",
6183 description: "The directory path to use (will be shell expanded).",
6184 field: Box::new(SettingField {
6185 json_path: Some("terminal.working_directory.always"),
6186 pick: |settings_content| {
6187 match settings_content.terminal.as_ref()?.project.working_directory.as_ref() {
6188 Some(settings::WorkingDirectory::Always { directory }) => Some(directory),
6189 _ => None,
6190 }
6191 },
6192 write: |settings_content, value| {
6193 let value = value.unwrap_or_default();
6194 match settings_content
6195 .terminal
6196 .get_or_insert_default()
6197 .project
6198 .working_directory
6199 .as_mut()
6200 {
6201 Some(settings::WorkingDirectory::Always { directory }) => *directory = value,
6202 _ => return,
6203 }
6204 },
6205 }),
6206 metadata: None,
6207 }],
6208 })
6209 .collect(),
6210 }),
6211 SettingsPageItem::SettingItem(SettingItem {
6212 title: "Environment Variables",
6213 description: "Key-value pairs to add to the terminal's environment.",
6214 field: Box::new(
6215 SettingField {
6216 json_path: Some("terminal.env"),
6217 pick: |settings_content| settings_content.terminal.as_ref()?.project.env.as_ref(),
6218 write: |settings_content, value| {
6219 settings_content.terminal.get_or_insert_default().project.env = value;
6220 },
6221 }
6222 .unimplemented(),
6223 ),
6224 metadata: None,
6225 files: USER | PROJECT,
6226 }),
6227 SettingsPageItem::SettingItem(SettingItem {
6228 title: "Detect Virtual Environment",
6229 description: "Activates the Python virtual environment, if one is found, in the terminal's working directory.",
6230 field: Box::new(
6231 SettingField {
6232 json_path: Some("terminal.detect_venv"),
6233 pick: |settings_content| settings_content.terminal.as_ref()?.project.detect_venv.as_ref(),
6234 write: |settings_content, value| {
6235 settings_content
6236 .terminal
6237 .get_or_insert_default()
6238 .project
6239 .detect_venv = value;
6240 },
6241 }
6242 .unimplemented(),
6243 ),
6244 metadata: None,
6245 files: USER | PROJECT,
6246 }),
6247 ]
6248 }
6249
6250 fn font_section() -> [SettingsPageItem; 6] {
6251 [
6252 SettingsPageItem::SectionHeader("Font"),
6253 SettingsPageItem::SettingItem(SettingItem {
6254 title: "Font Size",
6255 description: "Font size for terminal text. If not set, defaults to buffer font size.",
6256 field: Box::new(SettingField {
6257 json_path: Some("terminal.font_size"),
6258 pick: |settings_content| {
6259 settings_content
6260 .terminal
6261 .as_ref()
6262 .and_then(|terminal| terminal.font_size.as_ref())
6263 .or(settings_content.theme.buffer_font_size.as_ref())
6264 },
6265 write: |settings_content, value| {
6266 settings_content.terminal.get_or_insert_default().font_size = value;
6267 },
6268 }),
6269 metadata: None,
6270 files: USER,
6271 }),
6272 SettingsPageItem::SettingItem(SettingItem {
6273 title: "Font Family",
6274 description: "Font family for terminal text. If not set, defaults to buffer font family.",
6275 field: Box::new(SettingField {
6276 json_path: Some("terminal.font_family"),
6277 pick: |settings_content| {
6278 settings_content
6279 .terminal
6280 .as_ref()
6281 .and_then(|terminal| terminal.font_family.as_ref())
6282 .or(settings_content.theme.buffer_font_family.as_ref())
6283 },
6284 write: |settings_content, value| {
6285 settings_content
6286 .terminal
6287 .get_or_insert_default()
6288 .font_family = value;
6289 },
6290 }),
6291 metadata: None,
6292 files: USER,
6293 }),
6294 SettingsPageItem::SettingItem(SettingItem {
6295 title: "Font Fallbacks",
6296 description: "Font fallbacks for terminal text. If not set, defaults to buffer font fallbacks.",
6297 field: Box::new(
6298 SettingField {
6299 json_path: Some("terminal.font_fallbacks"),
6300 pick: |settings_content| {
6301 settings_content
6302 .terminal
6303 .as_ref()
6304 .and_then(|terminal| terminal.font_fallbacks.as_ref())
6305 .or(settings_content.theme.buffer_font_fallbacks.as_ref())
6306 },
6307 write: |settings_content, value| {
6308 settings_content
6309 .terminal
6310 .get_or_insert_default()
6311 .font_fallbacks = value;
6312 },
6313 }
6314 .unimplemented(),
6315 ),
6316 metadata: None,
6317 files: USER,
6318 }),
6319 SettingsPageItem::SettingItem(SettingItem {
6320 title: "Font Weight",
6321 description: "Font weight for terminal text in CSS weight units (100-900).",
6322 field: Box::new(SettingField {
6323 json_path: Some("terminal.font_weight"),
6324 pick: |settings_content| {
6325 settings_content.terminal.as_ref()?.font_weight.as_ref()
6326 },
6327 write: |settings_content, value| {
6328 settings_content
6329 .terminal
6330 .get_or_insert_default()
6331 .font_weight = value;
6332 },
6333 }),
6334 metadata: None,
6335 files: USER,
6336 }),
6337 SettingsPageItem::SettingItem(SettingItem {
6338 title: "Font Features",
6339 description: "Font features for terminal text.",
6340 field: Box::new(
6341 SettingField {
6342 json_path: Some("terminal.font_features"),
6343 pick: |settings_content| {
6344 settings_content
6345 .terminal
6346 .as_ref()
6347 .and_then(|terminal| terminal.font_features.as_ref())
6348 .or(settings_content.theme.buffer_font_features.as_ref())
6349 },
6350 write: |settings_content, value| {
6351 settings_content
6352 .terminal
6353 .get_or_insert_default()
6354 .font_features = value;
6355 },
6356 }
6357 .unimplemented(),
6358 ),
6359 metadata: None,
6360 files: USER,
6361 }),
6362 ]
6363 }
6364
6365 fn display_settings_section() -> [SettingsPageItem; 6] {
6366 [
6367 SettingsPageItem::SectionHeader("Display Settings"),
6368 SettingsPageItem::SettingItem(SettingItem {
6369 title: "Line Height",
6370 description: "Line height for terminal text.",
6371 field: Box::new(
6372 SettingField {
6373 json_path: Some("terminal.line_height"),
6374 pick: |settings_content| {
6375 settings_content.terminal.as_ref()?.line_height.as_ref()
6376 },
6377 write: |settings_content, value| {
6378 settings_content
6379 .terminal
6380 .get_or_insert_default()
6381 .line_height = value;
6382 },
6383 }
6384 .unimplemented(),
6385 ),
6386 metadata: None,
6387 files: USER,
6388 }),
6389 SettingsPageItem::SettingItem(SettingItem {
6390 title: "Cursor Shape",
6391 description: "Default cursor shape for the terminal (bar, block, underline, or hollow).",
6392 field: Box::new(SettingField {
6393 json_path: Some("terminal.cursor_shape"),
6394 pick: |settings_content| {
6395 settings_content.terminal.as_ref()?.cursor_shape.as_ref()
6396 },
6397 write: |settings_content, value| {
6398 settings_content
6399 .terminal
6400 .get_or_insert_default()
6401 .cursor_shape = value;
6402 },
6403 }),
6404 metadata: None,
6405 files: USER,
6406 }),
6407 SettingsPageItem::SettingItem(SettingItem {
6408 title: "Cursor Blinking",
6409 description: "Sets the cursor blinking behavior in the terminal.",
6410 field: Box::new(SettingField {
6411 json_path: Some("terminal.blinking"),
6412 pick: |settings_content| settings_content.terminal.as_ref()?.blinking.as_ref(),
6413 write: |settings_content, value| {
6414 settings_content.terminal.get_or_insert_default().blinking = value;
6415 },
6416 }),
6417 metadata: None,
6418 files: USER,
6419 }),
6420 SettingsPageItem::SettingItem(SettingItem {
6421 title: "Alternate Scroll",
6422 description: "Whether alternate scroll mode is active by default (converts mouse scroll to arrow keys in apps like Vim).",
6423 field: Box::new(SettingField {
6424 json_path: Some("terminal.alternate_scroll"),
6425 pick: |settings_content| {
6426 settings_content
6427 .terminal
6428 .as_ref()?
6429 .alternate_scroll
6430 .as_ref()
6431 },
6432 write: |settings_content, value| {
6433 settings_content
6434 .terminal
6435 .get_or_insert_default()
6436 .alternate_scroll = value;
6437 },
6438 }),
6439 metadata: None,
6440 files: USER,
6441 }),
6442 SettingsPageItem::SettingItem(SettingItem {
6443 title: "Minimum Contrast",
6444 description: "The minimum APCA perceptual contrast between foreground and background colors (0-106).",
6445 field: Box::new(SettingField {
6446 json_path: Some("terminal.minimum_contrast"),
6447 pick: |settings_content| {
6448 settings_content
6449 .terminal
6450 .as_ref()?
6451 .minimum_contrast
6452 .as_ref()
6453 },
6454 write: |settings_content, value| {
6455 settings_content
6456 .terminal
6457 .get_or_insert_default()
6458 .minimum_contrast = value;
6459 },
6460 }),
6461 metadata: None,
6462 files: USER,
6463 }),
6464 ]
6465 }
6466
6467 fn behavior_settings_section() -> [SettingsPageItem; 4] {
6468 [
6469 SettingsPageItem::SectionHeader("Behavior Settings"),
6470 SettingsPageItem::SettingItem(SettingItem {
6471 title: "Option As Meta",
6472 description: "Whether the option key behaves as the meta key.",
6473 field: Box::new(SettingField {
6474 json_path: Some("terminal.option_as_meta"),
6475 pick: |settings_content| {
6476 settings_content.terminal.as_ref()?.option_as_meta.as_ref()
6477 },
6478 write: |settings_content, value| {
6479 settings_content
6480 .terminal
6481 .get_or_insert_default()
6482 .option_as_meta = value;
6483 },
6484 }),
6485 metadata: None,
6486 files: USER,
6487 }),
6488 SettingsPageItem::SettingItem(SettingItem {
6489 title: "Copy On Select",
6490 description: "Whether selecting text in the terminal automatically copies to the system clipboard.",
6491 field: Box::new(SettingField {
6492 json_path: Some("terminal.copy_on_select"),
6493 pick: |settings_content| {
6494 settings_content.terminal.as_ref()?.copy_on_select.as_ref()
6495 },
6496 write: |settings_content, value| {
6497 settings_content
6498 .terminal
6499 .get_or_insert_default()
6500 .copy_on_select = value;
6501 },
6502 }),
6503 metadata: None,
6504 files: USER,
6505 }),
6506 SettingsPageItem::SettingItem(SettingItem {
6507 title: "Keep Selection On Copy",
6508 description: "Whether to keep the text selection after copying it to the clipboard.",
6509 field: Box::new(SettingField {
6510 json_path: Some("terminal.keep_selection_on_copy"),
6511 pick: |settings_content| {
6512 settings_content
6513 .terminal
6514 .as_ref()?
6515 .keep_selection_on_copy
6516 .as_ref()
6517 },
6518 write: |settings_content, value| {
6519 settings_content
6520 .terminal
6521 .get_or_insert_default()
6522 .keep_selection_on_copy = value;
6523 },
6524 }),
6525 metadata: None,
6526 files: USER,
6527 }),
6528 ]
6529 }
6530
6531 fn layout_settings_section() -> [SettingsPageItem; 3] {
6532 [
6533 SettingsPageItem::SectionHeader("Layout Settings"),
6534 SettingsPageItem::SettingItem(SettingItem {
6535 title: "Default Width",
6536 description: "Default width when the terminal is docked to the left or right (in pixels).",
6537 field: Box::new(SettingField {
6538 json_path: Some("terminal.default_width"),
6539 pick: |settings_content| {
6540 settings_content.terminal.as_ref()?.default_width.as_ref()
6541 },
6542 write: |settings_content, value| {
6543 settings_content
6544 .terminal
6545 .get_or_insert_default()
6546 .default_width = value;
6547 },
6548 }),
6549 metadata: None,
6550 files: USER,
6551 }),
6552 SettingsPageItem::SettingItem(SettingItem {
6553 title: "Default Height",
6554 description: "Default height when the terminal is docked to the bottom (in pixels).",
6555 field: Box::new(SettingField {
6556 json_path: Some("terminal.default_height"),
6557 pick: |settings_content| {
6558 settings_content.terminal.as_ref()?.default_height.as_ref()
6559 },
6560 write: |settings_content, value| {
6561 settings_content
6562 .terminal
6563 .get_or_insert_default()
6564 .default_height = value;
6565 },
6566 }),
6567 metadata: None,
6568 files: USER,
6569 }),
6570 ]
6571 }
6572
6573 fn advanced_settings_section() -> [SettingsPageItem; 3] {
6574 [
6575 SettingsPageItem::SectionHeader("Advanced Settings"),
6576 SettingsPageItem::SettingItem(SettingItem {
6577 title: "Max Scroll History Lines",
6578 description: "Maximum number of lines to keep in scrollback history (max: 100,000; 0 disables scrolling).",
6579 field: Box::new(SettingField {
6580 json_path: Some("terminal.max_scroll_history_lines"),
6581 pick: |settings_content| {
6582 settings_content
6583 .terminal
6584 .as_ref()?
6585 .max_scroll_history_lines
6586 .as_ref()
6587 },
6588 write: |settings_content, value| {
6589 settings_content
6590 .terminal
6591 .get_or_insert_default()
6592 .max_scroll_history_lines = value;
6593 },
6594 }),
6595 metadata: None,
6596 files: USER,
6597 }),
6598 SettingsPageItem::SettingItem(SettingItem {
6599 title: "Scroll Multiplier",
6600 description: "The multiplier for scrolling in the terminal with the mouse wheel",
6601 field: Box::new(SettingField {
6602 json_path: Some("terminal.scroll_multiplier"),
6603 pick: |settings_content| {
6604 settings_content
6605 .terminal
6606 .as_ref()?
6607 .scroll_multiplier
6608 .as_ref()
6609 },
6610 write: |settings_content, value| {
6611 settings_content
6612 .terminal
6613 .get_or_insert_default()
6614 .scroll_multiplier = value;
6615 },
6616 }),
6617 metadata: None,
6618 files: USER,
6619 }),
6620 ]
6621 }
6622
6623 fn toolbar_section() -> [SettingsPageItem; 2] {
6624 [
6625 SettingsPageItem::SectionHeader("Toolbar"),
6626 SettingsPageItem::SettingItem(SettingItem {
6627 title: "Breadcrumbs",
6628 description: "Display the terminal title in breadcrumbs inside the terminal pane.",
6629 field: Box::new(SettingField {
6630 json_path: Some("terminal.toolbar.breadcrumbs"),
6631 pick: |settings_content| {
6632 settings_content
6633 .terminal
6634 .as_ref()?
6635 .toolbar
6636 .as_ref()?
6637 .breadcrumbs
6638 .as_ref()
6639 },
6640 write: |settings_content, value| {
6641 settings_content
6642 .terminal
6643 .get_or_insert_default()
6644 .toolbar
6645 .get_or_insert_default()
6646 .breadcrumbs = value;
6647 },
6648 }),
6649 metadata: None,
6650 files: USER,
6651 }),
6652 ]
6653 }
6654
6655 fn scrollbar_section() -> [SettingsPageItem; 2] {
6656 [
6657 SettingsPageItem::SectionHeader("Scrollbar"),
6658 SettingsPageItem::SettingItem(SettingItem {
6659 title: "Show Scrollbar",
6660 description: "When to show the scrollbar in the terminal.",
6661 field: Box::new(SettingField {
6662 json_path: Some("terminal.scrollbar.show"),
6663 pick: |settings_content| {
6664 show_scrollbar_or_editor(settings_content, |settings_content| {
6665 settings_content
6666 .terminal
6667 .as_ref()?
6668 .scrollbar
6669 .as_ref()?
6670 .show
6671 .as_ref()
6672 })
6673 },
6674 write: |settings_content, value| {
6675 settings_content
6676 .terminal
6677 .get_or_insert_default()
6678 .scrollbar
6679 .get_or_insert_default()
6680 .show = value;
6681 },
6682 }),
6683 metadata: None,
6684 files: USER,
6685 }),
6686 ]
6687 }
6688
6689 SettingsPage {
6690 title: "Terminal",
6691 items: concat_sections![
6692 environment_section(),
6693 font_section(),
6694 display_settings_section(),
6695 behavior_settings_section(),
6696 layout_settings_section(),
6697 advanced_settings_section(),
6698 toolbar_section(),
6699 scrollbar_section(),
6700 ],
6701 }
6702}
6703
6704fn version_control_page() -> SettingsPage {
6705 fn git_integration_section() -> [SettingsPageItem; 2] {
6706 [
6707 SettingsPageItem::SectionHeader("Git Integration"),
6708 SettingsPageItem::DynamicItem(DynamicItem {
6709 discriminant: SettingItem {
6710 files: USER,
6711 title: "Disable Git Integration",
6712 description: "Disable all Git integration features in Zed.",
6713 field: Box::new(SettingField::<bool> {
6714 json_path: Some("git.disable_git"),
6715 pick: |settings_content| {
6716 settings_content
6717 .git
6718 .as_ref()?
6719 .enabled
6720 .as_ref()?
6721 .disable_git
6722 .as_ref()
6723 },
6724 write: |settings_content, value| {
6725 settings_content
6726 .git
6727 .get_or_insert_default()
6728 .enabled
6729 .get_or_insert_default()
6730 .disable_git = value;
6731 },
6732 }),
6733 metadata: None,
6734 },
6735 pick_discriminant: |settings_content| {
6736 let disabled = settings_content
6737 .git
6738 .as_ref()?
6739 .enabled
6740 .as_ref()?
6741 .disable_git
6742 .unwrap_or(false);
6743 Some(if disabled { 0 } else { 1 })
6744 },
6745 fields: vec![
6746 vec![],
6747 vec![
6748 SettingItem {
6749 files: USER,
6750 title: "Enable Git Status",
6751 description: "Show Git status information in the editor.",
6752 field: Box::new(SettingField::<bool> {
6753 json_path: Some("git.enable_status"),
6754 pick: |settings_content| {
6755 settings_content
6756 .git
6757 .as_ref()?
6758 .enabled
6759 .as_ref()?
6760 .enable_status
6761 .as_ref()
6762 },
6763 write: |settings_content, value| {
6764 settings_content
6765 .git
6766 .get_or_insert_default()
6767 .enabled
6768 .get_or_insert_default()
6769 .enable_status = value;
6770 },
6771 }),
6772 metadata: None,
6773 },
6774 SettingItem {
6775 files: USER,
6776 title: "Enable Git Diff",
6777 description: "Show Git diff information in the editor.",
6778 field: Box::new(SettingField::<bool> {
6779 json_path: Some("git.enable_diff"),
6780 pick: |settings_content| {
6781 settings_content
6782 .git
6783 .as_ref()?
6784 .enabled
6785 .as_ref()?
6786 .enable_diff
6787 .as_ref()
6788 },
6789 write: |settings_content, value| {
6790 settings_content
6791 .git
6792 .get_or_insert_default()
6793 .enabled
6794 .get_or_insert_default()
6795 .enable_diff = value;
6796 },
6797 }),
6798 metadata: None,
6799 },
6800 ],
6801 ],
6802 }),
6803 ]
6804 }
6805
6806 fn git_gutter_section() -> [SettingsPageItem; 3] {
6807 [
6808 SettingsPageItem::SectionHeader("Git Gutter"),
6809 SettingsPageItem::SettingItem(SettingItem {
6810 title: "Visibility",
6811 description: "Control whether Git status is shown in the editor's gutter.",
6812 field: Box::new(SettingField {
6813 json_path: Some("git.git_gutter"),
6814 pick: |settings_content| settings_content.git.as_ref()?.git_gutter.as_ref(),
6815 write: |settings_content, value| {
6816 settings_content.git.get_or_insert_default().git_gutter = value;
6817 },
6818 }),
6819 metadata: None,
6820 files: USER,
6821 }),
6822 // todo(settings_ui): Figure out the right default for this value in default.json
6823 SettingsPageItem::SettingItem(SettingItem {
6824 title: "Debounce",
6825 description: "Debounce threshold in milliseconds after which changes are reflected in the Git gutter.",
6826 field: Box::new(SettingField {
6827 json_path: Some("git.gutter_debounce"),
6828 pick: |settings_content| {
6829 settings_content.git.as_ref()?.gutter_debounce.as_ref()
6830 },
6831 write: |settings_content, value| {
6832 settings_content.git.get_or_insert_default().gutter_debounce = value;
6833 },
6834 }),
6835 metadata: None,
6836 files: USER,
6837 }),
6838 ]
6839 }
6840
6841 fn inline_git_blame_section() -> [SettingsPageItem; 6] {
6842 [
6843 SettingsPageItem::SectionHeader("Inline Git Blame"),
6844 SettingsPageItem::SettingItem(SettingItem {
6845 title: "Enabled",
6846 description: "Whether or not to show Git blame data inline in the currently focused line.",
6847 field: Box::new(SettingField {
6848 json_path: Some("git.inline_blame.enabled"),
6849 pick: |settings_content| {
6850 settings_content
6851 .git
6852 .as_ref()?
6853 .inline_blame
6854 .as_ref()?
6855 .enabled
6856 .as_ref()
6857 },
6858 write: |settings_content, value| {
6859 settings_content
6860 .git
6861 .get_or_insert_default()
6862 .inline_blame
6863 .get_or_insert_default()
6864 .enabled = value;
6865 },
6866 }),
6867 metadata: None,
6868 files: USER,
6869 }),
6870 SettingsPageItem::SettingItem(SettingItem {
6871 title: "Delay",
6872 description: "The delay after which the inline blame information is shown.",
6873 field: Box::new(SettingField {
6874 json_path: Some("git.inline_blame.delay_ms"),
6875 pick: |settings_content| {
6876 settings_content
6877 .git
6878 .as_ref()?
6879 .inline_blame
6880 .as_ref()?
6881 .delay_ms
6882 .as_ref()
6883 },
6884 write: |settings_content, value| {
6885 settings_content
6886 .git
6887 .get_or_insert_default()
6888 .inline_blame
6889 .get_or_insert_default()
6890 .delay_ms = value;
6891 },
6892 }),
6893 metadata: None,
6894 files: USER,
6895 }),
6896 SettingsPageItem::SettingItem(SettingItem {
6897 title: "Padding",
6898 description: "Padding between the end of the source line and the start of the inline blame in columns.",
6899 field: Box::new(SettingField {
6900 json_path: Some("git.inline_blame.padding"),
6901 pick: |settings_content| {
6902 settings_content
6903 .git
6904 .as_ref()?
6905 .inline_blame
6906 .as_ref()?
6907 .padding
6908 .as_ref()
6909 },
6910 write: |settings_content, value| {
6911 settings_content
6912 .git
6913 .get_or_insert_default()
6914 .inline_blame
6915 .get_or_insert_default()
6916 .padding = value;
6917 },
6918 }),
6919 metadata: None,
6920 files: USER,
6921 }),
6922 SettingsPageItem::SettingItem(SettingItem {
6923 title: "Minimum Column",
6924 description: "The minimum column number at which to show the inline blame information.",
6925 field: Box::new(SettingField {
6926 json_path: Some("git.inline_blame.min_column"),
6927 pick: |settings_content| {
6928 settings_content
6929 .git
6930 .as_ref()?
6931 .inline_blame
6932 .as_ref()?
6933 .min_column
6934 .as_ref()
6935 },
6936 write: |settings_content, value| {
6937 settings_content
6938 .git
6939 .get_or_insert_default()
6940 .inline_blame
6941 .get_or_insert_default()
6942 .min_column = value;
6943 },
6944 }),
6945 metadata: None,
6946 files: USER,
6947 }),
6948 SettingsPageItem::SettingItem(SettingItem {
6949 title: "Show Commit Summary",
6950 description: "Show commit summary as part of the inline blame.",
6951 field: Box::new(SettingField {
6952 json_path: Some("git.inline_blame.show_commit_summary"),
6953 pick: |settings_content| {
6954 settings_content
6955 .git
6956 .as_ref()?
6957 .inline_blame
6958 .as_ref()?
6959 .show_commit_summary
6960 .as_ref()
6961 },
6962 write: |settings_content, value| {
6963 settings_content
6964 .git
6965 .get_or_insert_default()
6966 .inline_blame
6967 .get_or_insert_default()
6968 .show_commit_summary = value;
6969 },
6970 }),
6971 metadata: None,
6972 files: USER,
6973 }),
6974 ]
6975 }
6976
6977 fn git_blame_view_section() -> [SettingsPageItem; 2] {
6978 [
6979 SettingsPageItem::SectionHeader("Git Blame View"),
6980 SettingsPageItem::SettingItem(SettingItem {
6981 title: "Show Avatar",
6982 description: "Show the avatar of the author of the commit.",
6983 field: Box::new(SettingField {
6984 json_path: Some("git.blame.show_avatar"),
6985 pick: |settings_content| {
6986 settings_content
6987 .git
6988 .as_ref()?
6989 .blame
6990 .as_ref()?
6991 .show_avatar
6992 .as_ref()
6993 },
6994 write: |settings_content, value| {
6995 settings_content
6996 .git
6997 .get_or_insert_default()
6998 .blame
6999 .get_or_insert_default()
7000 .show_avatar = value;
7001 },
7002 }),
7003 metadata: None,
7004 files: USER,
7005 }),
7006 ]
7007 }
7008
7009 fn branch_picker_section() -> [SettingsPageItem; 2] {
7010 [
7011 SettingsPageItem::SectionHeader("Branch Picker"),
7012 SettingsPageItem::SettingItem(SettingItem {
7013 title: "Show Author Name",
7014 description: "Show author name as part of the commit information in branch picker.",
7015 field: Box::new(SettingField {
7016 json_path: Some("git.branch_picker.show_author_name"),
7017 pick: |settings_content| {
7018 settings_content
7019 .git
7020 .as_ref()?
7021 .branch_picker
7022 .as_ref()?
7023 .show_author_name
7024 .as_ref()
7025 },
7026 write: |settings_content, value| {
7027 settings_content
7028 .git
7029 .get_or_insert_default()
7030 .branch_picker
7031 .get_or_insert_default()
7032 .show_author_name = value;
7033 },
7034 }),
7035 metadata: None,
7036 files: USER,
7037 }),
7038 ]
7039 }
7040
7041 fn git_hunks_section() -> [SettingsPageItem; 3] {
7042 [
7043 SettingsPageItem::SectionHeader("Git Hunks"),
7044 SettingsPageItem::SettingItem(SettingItem {
7045 title: "Hunk Style",
7046 description: "How Git hunks are displayed visually in the editor.",
7047 field: Box::new(SettingField {
7048 json_path: Some("git.hunk_style"),
7049 pick: |settings_content| settings_content.git.as_ref()?.hunk_style.as_ref(),
7050 write: |settings_content, value| {
7051 settings_content.git.get_or_insert_default().hunk_style = value;
7052 },
7053 }),
7054 metadata: None,
7055 files: USER,
7056 }),
7057 SettingsPageItem::SettingItem(SettingItem {
7058 title: "Path Style",
7059 description: "Should the name or path be displayed first in the git view.",
7060 field: Box::new(SettingField {
7061 json_path: Some("git.path_style"),
7062 pick: |settings_content| settings_content.git.as_ref()?.path_style.as_ref(),
7063 write: |settings_content, value| {
7064 settings_content.git.get_or_insert_default().path_style = value;
7065 },
7066 }),
7067 metadata: None,
7068 files: USER,
7069 }),
7070 ]
7071 }
7072
7073 SettingsPage {
7074 title: "Version Control",
7075 items: concat_sections![
7076 git_integration_section(),
7077 git_gutter_section(),
7078 inline_git_blame_section(),
7079 git_blame_view_section(),
7080 branch_picker_section(),
7081 git_hunks_section(),
7082 ],
7083 }
7084}
7085
7086fn collaboration_page() -> SettingsPage {
7087 fn calls_section() -> [SettingsPageItem; 3] {
7088 [
7089 SettingsPageItem::SectionHeader("Calls"),
7090 SettingsPageItem::SettingItem(SettingItem {
7091 title: "Mute On Join",
7092 description: "Whether the microphone should be muted when joining a channel or a call.",
7093 field: Box::new(SettingField {
7094 json_path: Some("calls.mute_on_join"),
7095 pick: |settings_content| settings_content.calls.as_ref()?.mute_on_join.as_ref(),
7096 write: |settings_content, value| {
7097 settings_content.calls.get_or_insert_default().mute_on_join = value;
7098 },
7099 }),
7100 metadata: None,
7101 files: USER,
7102 }),
7103 SettingsPageItem::SettingItem(SettingItem {
7104 title: "Share On Join",
7105 description: "Whether your current project should be shared when joining an empty channel.",
7106 field: Box::new(SettingField {
7107 json_path: Some("calls.share_on_join"),
7108 pick: |settings_content| {
7109 settings_content.calls.as_ref()?.share_on_join.as_ref()
7110 },
7111 write: |settings_content, value| {
7112 settings_content.calls.get_or_insert_default().share_on_join = value;
7113 },
7114 }),
7115 metadata: None,
7116 files: USER,
7117 }),
7118 ]
7119 }
7120
7121 fn audio_settings() -> [SettingsPageItem; 3] {
7122 [
7123 SettingsPageItem::ActionLink(ActionLink {
7124 title: "Test Audio".into(),
7125 description: Some("Test your microphone and speaker setup".into()),
7126 button_text: "Test Audio".into(),
7127 on_click: Arc::new(|_settings_window, window, cx| {
7128 open_audio_test_window(window, cx);
7129 }),
7130 files: USER,
7131 }),
7132 SettingsPageItem::SettingItem(SettingItem {
7133 title: "Output Audio Device",
7134 description: "Select output audio device",
7135 field: Box::new(SettingField {
7136 json_path: Some("audio.experimental.output_audio_device"),
7137 pick: |settings_content| {
7138 settings_content
7139 .audio
7140 .as_ref()?
7141 .output_audio_device
7142 .as_ref()
7143 .or(DEFAULT_EMPTY_AUDIO_OUTPUT)
7144 },
7145 write: |settings_content, value| {
7146 settings_content
7147 .audio
7148 .get_or_insert_default()
7149 .output_audio_device = value;
7150 },
7151 }),
7152 metadata: None,
7153 files: USER,
7154 }),
7155 SettingsPageItem::SettingItem(SettingItem {
7156 title: "Input Audio Device",
7157 description: "Select input audio device",
7158 field: Box::new(SettingField {
7159 json_path: Some("audio.experimental.input_audio_device"),
7160 pick: |settings_content| {
7161 settings_content
7162 .audio
7163 .as_ref()?
7164 .input_audio_device
7165 .as_ref()
7166 .or(DEFAULT_EMPTY_AUDIO_INPUT)
7167 },
7168 write: |settings_content, value| {
7169 settings_content
7170 .audio
7171 .get_or_insert_default()
7172 .input_audio_device = value;
7173 },
7174 }),
7175 metadata: None,
7176 files: USER,
7177 }),
7178 ]
7179 }
7180
7181 SettingsPage {
7182 title: "Collaboration",
7183 items: concat_sections![calls_section(), audio_settings()],
7184 }
7185}
7186
7187fn ai_page(cx: &App) -> SettingsPage {
7188 fn general_section() -> [SettingsPageItem; 2] {
7189 [
7190 SettingsPageItem::SectionHeader("General"),
7191 SettingsPageItem::SettingItem(SettingItem {
7192 title: "Disable AI",
7193 description: "Whether to disable all AI features in Zed.",
7194 field: Box::new(SettingField {
7195 json_path: Some("disable_ai"),
7196 pick: |settings_content| settings_content.project.disable_ai.as_ref(),
7197 write: |settings_content, value| {
7198 settings_content.project.disable_ai = value;
7199 },
7200 }),
7201 metadata: None,
7202 files: USER | PROJECT,
7203 }),
7204 ]
7205 }
7206
7207 fn agent_configuration_section(cx: &App) -> Box<[SettingsPageItem]> {
7208 let mut items = vec![
7209 SettingsPageItem::SectionHeader("Agent Configuration"),
7210 SettingsPageItem::SubPageLink(SubPageLink {
7211 title: "Tool Permissions".into(),
7212 r#type: Default::default(),
7213 json_path: Some("agent.tool_permissions"),
7214 description: Some("Set up regex patterns to auto-allow, auto-deny, or always request confirmation, for specific tool inputs.".into()),
7215 in_json: true,
7216 files: USER,
7217 render: render_tool_permissions_setup_page,
7218 }),
7219 ];
7220
7221 if cx.has_flag::<AgentV2FeatureFlag>() {
7222 items.push(SettingsPageItem::SettingItem(SettingItem {
7223 title: "New Thread Location",
7224 description: "Whether to start a new thread in the current local project or in a new Git worktree.",
7225 field: Box::new(SettingField {
7226 json_path: Some("agent.new_thread_location"),
7227 pick: |settings_content| {
7228 settings_content
7229 .agent
7230 .as_ref()?
7231 .new_thread_location
7232 .as_ref()
7233 },
7234 write: |settings_content, value| {
7235 settings_content
7236 .agent
7237 .get_or_insert_default()
7238 .new_thread_location = value;
7239 },
7240 }),
7241 metadata: None,
7242 files: USER,
7243 }));
7244 }
7245
7246 items.extend([
7247 SettingsPageItem::SettingItem(SettingItem {
7248 title: "Single File Review",
7249 description: "When enabled, agent edits will also be displayed in single-file buffers for review.",
7250 field: Box::new(SettingField {
7251 json_path: Some("agent.single_file_review"),
7252 pick: |settings_content| {
7253 settings_content.agent.as_ref()?.single_file_review.as_ref()
7254 },
7255 write: |settings_content, value| {
7256 settings_content
7257 .agent
7258 .get_or_insert_default()
7259 .single_file_review = value;
7260 },
7261 }),
7262 metadata: None,
7263 files: USER,
7264 }),
7265 SettingsPageItem::SettingItem(SettingItem {
7266 title: "Enable Feedback",
7267 description: "Show voting thumbs up/down icon buttons for feedback on agent edits.",
7268 field: Box::new(SettingField {
7269 json_path: Some("agent.enable_feedback"),
7270 pick: |settings_content| {
7271 settings_content.agent.as_ref()?.enable_feedback.as_ref()
7272 },
7273 write: |settings_content, value| {
7274 settings_content
7275 .agent
7276 .get_or_insert_default()
7277 .enable_feedback = value;
7278 },
7279 }),
7280 metadata: None,
7281 files: USER,
7282 }),
7283 SettingsPageItem::SettingItem(SettingItem {
7284 title: "Notify When Agent Waiting",
7285 description: "Where to show notifications when the agent has completed its response or needs confirmation before running a tool action.",
7286 field: Box::new(SettingField {
7287 json_path: Some("agent.notify_when_agent_waiting"),
7288 pick: |settings_content| {
7289 settings_content
7290 .agent
7291 .as_ref()?
7292 .notify_when_agent_waiting
7293 .as_ref()
7294 },
7295 write: |settings_content, value| {
7296 settings_content
7297 .agent
7298 .get_or_insert_default()
7299 .notify_when_agent_waiting = value;
7300 },
7301 }),
7302 metadata: None,
7303 files: USER,
7304 }),
7305 SettingsPageItem::SettingItem(SettingItem {
7306 title: "Play Sound When Agent Done",
7307 description: "When to play a sound when the agent has either completed its response, or needs user input.",
7308 field: Box::new(SettingField {
7309 json_path: Some("agent.play_sound_when_agent_done"),
7310 pick: |settings_content| {
7311 settings_content
7312 .agent
7313 .as_ref()?
7314 .play_sound_when_agent_done
7315 .as_ref()
7316 },
7317 write: |settings_content, value| {
7318 settings_content
7319 .agent
7320 .get_or_insert_default()
7321 .play_sound_when_agent_done = value;
7322 },
7323 }),
7324 metadata: None,
7325 files: USER,
7326 }),
7327 SettingsPageItem::SettingItem(SettingItem {
7328 title: "Expand Edit Card",
7329 description: "Whether to have edit cards in the agent panel expanded, showing a Preview of the diff.",
7330 field: Box::new(SettingField {
7331 json_path: Some("agent.expand_edit_card"),
7332 pick: |settings_content| {
7333 settings_content.agent.as_ref()?.expand_edit_card.as_ref()
7334 },
7335 write: |settings_content, value| {
7336 settings_content
7337 .agent
7338 .get_or_insert_default()
7339 .expand_edit_card = value;
7340 },
7341 }),
7342 metadata: None,
7343 files: USER,
7344 }),
7345 SettingsPageItem::SettingItem(SettingItem {
7346 title: "Expand Terminal Card",
7347 description: "Whether to have terminal cards in the agent panel expanded, showing the whole command output.",
7348 field: Box::new(SettingField {
7349 json_path: Some("agent.expand_terminal_card"),
7350 pick: |settings_content| {
7351 settings_content
7352 .agent
7353 .as_ref()?
7354 .expand_terminal_card
7355 .as_ref()
7356 },
7357 write: |settings_content, value| {
7358 settings_content
7359 .agent
7360 .get_or_insert_default()
7361 .expand_terminal_card = value;
7362 },
7363 }),
7364 metadata: None,
7365 files: USER,
7366 }),
7367 SettingsPageItem::SettingItem(SettingItem {
7368 title: "Thinking Display",
7369 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.",
7370 field: Box::new(SettingField {
7371 json_path: Some("agent.thinking_display"),
7372 pick: |settings_content| {
7373 settings_content
7374 .agent
7375 .as_ref()?
7376 .thinking_display
7377 .as_ref()
7378 },
7379 write: |settings_content, value| {
7380 settings_content
7381 .agent
7382 .get_or_insert_default()
7383 .thinking_display = value;
7384 },
7385 }),
7386 metadata: None,
7387 files: USER,
7388 }),
7389 SettingsPageItem::SettingItem(SettingItem {
7390 title: "Cancel Generation On Terminal Stop",
7391 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.",
7392 field: Box::new(SettingField {
7393 json_path: Some("agent.cancel_generation_on_terminal_stop"),
7394 pick: |settings_content| {
7395 settings_content
7396 .agent
7397 .as_ref()?
7398 .cancel_generation_on_terminal_stop
7399 .as_ref()
7400 },
7401 write: |settings_content, value| {
7402 settings_content
7403 .agent
7404 .get_or_insert_default()
7405 .cancel_generation_on_terminal_stop = value;
7406 },
7407 }),
7408 metadata: None,
7409 files: USER,
7410 }),
7411 SettingsPageItem::SettingItem(SettingItem {
7412 title: "Use Modifier To Send",
7413 description: "Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages.",
7414 field: Box::new(SettingField {
7415 json_path: Some("agent.use_modifier_to_send"),
7416 pick: |settings_content| {
7417 settings_content
7418 .agent
7419 .as_ref()?
7420 .use_modifier_to_send
7421 .as_ref()
7422 },
7423 write: |settings_content, value| {
7424 settings_content
7425 .agent
7426 .get_or_insert_default()
7427 .use_modifier_to_send = value;
7428 },
7429 }),
7430 metadata: None,
7431 files: USER,
7432 }),
7433 SettingsPageItem::SettingItem(SettingItem {
7434 title: "Message Editor Min Lines",
7435 description: "Minimum number of lines to display in the agent message editor.",
7436 field: Box::new(SettingField {
7437 json_path: Some("agent.message_editor_min_lines"),
7438 pick: |settings_content| {
7439 settings_content
7440 .agent
7441 .as_ref()?
7442 .message_editor_min_lines
7443 .as_ref()
7444 },
7445 write: |settings_content, value| {
7446 settings_content
7447 .agent
7448 .get_or_insert_default()
7449 .message_editor_min_lines = value;
7450 },
7451 }),
7452 metadata: None,
7453 files: USER,
7454 }),
7455 SettingsPageItem::SettingItem(SettingItem {
7456 title: "Show Turn Stats",
7457 description: "Whether to show turn statistics like elapsed time during generation and final turn duration.",
7458 field: Box::new(SettingField {
7459 json_path: Some("agent.show_turn_stats"),
7460 pick: |settings_content| {
7461 settings_content.agent.as_ref()?.show_turn_stats.as_ref()
7462 },
7463 write: |settings_content, value| {
7464 settings_content
7465 .agent
7466 .get_or_insert_default()
7467 .show_turn_stats = value;
7468 },
7469 }),
7470 metadata: None,
7471 files: USER,
7472 }),
7473 ]);
7474
7475 items.into_boxed_slice()
7476 }
7477
7478 fn context_servers_section() -> [SettingsPageItem; 2] {
7479 [
7480 SettingsPageItem::SectionHeader("Context Servers"),
7481 SettingsPageItem::SettingItem(SettingItem {
7482 title: "Context Server Timeout",
7483 description: "Default timeout in seconds for context server tool calls. Can be overridden per-server in context_servers configuration.",
7484 field: Box::new(SettingField {
7485 json_path: Some("context_server_timeout"),
7486 pick: |settings_content| {
7487 settings_content.project.context_server_timeout.as_ref()
7488 },
7489 write: |settings_content, value| {
7490 settings_content.project.context_server_timeout = value;
7491 },
7492 }),
7493 metadata: None,
7494 files: USER | PROJECT,
7495 }),
7496 ]
7497 }
7498
7499 fn edit_prediction_display_sub_section() -> [SettingsPageItem; 1] {
7500 [SettingsPageItem::SettingItem(SettingItem {
7501 title: "Display Mode",
7502 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.",
7503 field: Box::new(SettingField {
7504 json_path: Some("edit_prediction.display_mode"),
7505 pick: |settings_content| {
7506 settings_content
7507 .project
7508 .all_languages
7509 .edit_predictions
7510 .as_ref()?
7511 .mode
7512 .as_ref()
7513 },
7514 write: |settings_content, value| {
7515 settings_content
7516 .project
7517 .all_languages
7518 .edit_predictions
7519 .get_or_insert_default()
7520 .mode = value;
7521 },
7522 }),
7523 metadata: None,
7524 files: USER,
7525 })]
7526 }
7527
7528 SettingsPage {
7529 title: "AI",
7530 items: concat_sections![
7531 general_section(),
7532 agent_configuration_section(cx),
7533 context_servers_section(),
7534 edit_prediction_language_settings_section(),
7535 edit_prediction_display_sub_section()
7536 ],
7537 }
7538}
7539
7540fn network_page() -> SettingsPage {
7541 fn network_section() -> [SettingsPageItem; 3] {
7542 [
7543 SettingsPageItem::SectionHeader("Network"),
7544 SettingsPageItem::SettingItem(SettingItem {
7545 title: "Proxy",
7546 description: "The proxy to use for network requests.",
7547 field: Box::new(SettingField {
7548 json_path: Some("proxy"),
7549 pick: |settings_content| settings_content.proxy.as_ref(),
7550 write: |settings_content, value| {
7551 settings_content.proxy = value;
7552 },
7553 }),
7554 metadata: Some(Box::new(SettingsFieldMetadata {
7555 placeholder: Some("socks5h://localhost:10808"),
7556 ..Default::default()
7557 })),
7558 files: USER,
7559 }),
7560 SettingsPageItem::SettingItem(SettingItem {
7561 title: "Server URL",
7562 description: "The URL of the Zed server to connect to.",
7563 field: Box::new(SettingField {
7564 json_path: Some("server_url"),
7565 pick: |settings_content| settings_content.server_url.as_ref(),
7566 write: |settings_content, value| {
7567 settings_content.server_url = value;
7568 },
7569 }),
7570 metadata: Some(Box::new(SettingsFieldMetadata {
7571 placeholder: Some("https://zed.dev"),
7572 ..Default::default()
7573 })),
7574 files: USER,
7575 }),
7576 ]
7577 }
7578
7579 SettingsPage {
7580 title: "Network",
7581 items: concat_sections![network_section()],
7582 }
7583}
7584
7585fn language_settings_field<T>(
7586 settings_content: &SettingsContent,
7587 get_language_setting_field: fn(&LanguageSettingsContent) -> Option<&T>,
7588) -> Option<&T> {
7589 let all_languages = &settings_content.project.all_languages;
7590
7591 active_language()
7592 .and_then(|current_language_name| {
7593 all_languages
7594 .languages
7595 .0
7596 .get(current_language_name.as_ref())
7597 })
7598 .and_then(get_language_setting_field)
7599 .or_else(|| get_language_setting_field(&all_languages.defaults))
7600}
7601
7602fn language_settings_field_mut<T>(
7603 settings_content: &mut SettingsContent,
7604 value: Option<T>,
7605 write: fn(&mut LanguageSettingsContent, Option<T>),
7606) {
7607 let all_languages = &mut settings_content.project.all_languages;
7608 let language_content = if let Some(current_language) = active_language() {
7609 all_languages
7610 .languages
7611 .0
7612 .entry(current_language.to_string())
7613 .or_default()
7614 } else {
7615 &mut all_languages.defaults
7616 };
7617 write(language_content, value);
7618}
7619
7620fn language_settings_data() -> Box<[SettingsPageItem]> {
7621 fn indentation_section() -> [SettingsPageItem; 5] {
7622 [
7623 SettingsPageItem::SectionHeader("Indentation"),
7624 SettingsPageItem::SettingItem(SettingItem {
7625 title: "Tab Size",
7626 description: "How many columns a tab should occupy.",
7627 field: Box::new(SettingField {
7628 json_path: Some("languages.$(language).tab_size"), // TODO(cameron): not JQ syntax because not URL-safe
7629 pick: |settings_content| {
7630 language_settings_field(settings_content, |language| {
7631 language.tab_size.as_ref()
7632 })
7633 },
7634 write: |settings_content, value| {
7635 language_settings_field_mut(settings_content, value, |language, value| {
7636 language.tab_size = value;
7637 })
7638 },
7639 }),
7640 metadata: None,
7641 files: USER | PROJECT,
7642 }),
7643 SettingsPageItem::SettingItem(SettingItem {
7644 title: "Hard Tabs",
7645 description: "Whether to indent lines using tab characters, as opposed to multiple spaces.",
7646 field: Box::new(SettingField {
7647 json_path: Some("languages.$(language).hard_tabs"),
7648 pick: |settings_content| {
7649 language_settings_field(settings_content, |language| {
7650 language.hard_tabs.as_ref()
7651 })
7652 },
7653 write: |settings_content, value| {
7654 language_settings_field_mut(settings_content, value, |language, value| {
7655 language.hard_tabs = value;
7656 })
7657 },
7658 }),
7659 metadata: None,
7660 files: USER | PROJECT,
7661 }),
7662 SettingsPageItem::SettingItem(SettingItem {
7663 title: "Auto Indent",
7664 description: "Controls automatic indentation behavior when typing.",
7665 field: Box::new(SettingField {
7666 json_path: Some("languages.$(language).auto_indent"),
7667 pick: |settings_content| {
7668 language_settings_field(settings_content, |language| {
7669 language.auto_indent.as_ref()
7670 })
7671 },
7672 write: |settings_content, value| {
7673 language_settings_field_mut(settings_content, value, |language, value| {
7674 language.auto_indent = value;
7675 })
7676 },
7677 }),
7678 metadata: None,
7679 files: USER | PROJECT,
7680 }),
7681 SettingsPageItem::SettingItem(SettingItem {
7682 title: "Auto Indent On Paste",
7683 description: "Whether indentation of pasted content should be adjusted based on the context.",
7684 field: Box::new(SettingField {
7685 json_path: Some("languages.$(language).auto_indent_on_paste"),
7686 pick: |settings_content| {
7687 language_settings_field(settings_content, |language| {
7688 language.auto_indent_on_paste.as_ref()
7689 })
7690 },
7691 write: |settings_content, value| {
7692 language_settings_field_mut(settings_content, value, |language, value| {
7693 language.auto_indent_on_paste = value;
7694 })
7695 },
7696 }),
7697 metadata: None,
7698 files: USER | PROJECT,
7699 }),
7700 ]
7701 }
7702
7703 fn wrapping_section() -> [SettingsPageItem; 6] {
7704 [
7705 SettingsPageItem::SectionHeader("Wrapping"),
7706 SettingsPageItem::SettingItem(SettingItem {
7707 title: "Soft Wrap",
7708 description: "How to soft-wrap long lines of text.",
7709 field: Box::new(SettingField {
7710 json_path: Some("languages.$(language).soft_wrap"),
7711 pick: |settings_content| {
7712 language_settings_field(settings_content, |language| {
7713 language.soft_wrap.as_ref()
7714 })
7715 },
7716 write: |settings_content, value| {
7717 language_settings_field_mut(settings_content, value, |language, value| {
7718 language.soft_wrap = value;
7719 })
7720 },
7721 }),
7722 metadata: None,
7723 files: USER | PROJECT,
7724 }),
7725 SettingsPageItem::SettingItem(SettingItem {
7726 title: "Show Wrap Guides",
7727 description: "Show wrap guides in the editor.",
7728 field: Box::new(SettingField {
7729 json_path: Some("languages.$(language).show_wrap_guides"),
7730 pick: |settings_content| {
7731 language_settings_field(settings_content, |language| {
7732 language.show_wrap_guides.as_ref()
7733 })
7734 },
7735 write: |settings_content, value| {
7736 language_settings_field_mut(settings_content, value, |language, value| {
7737 language.show_wrap_guides = value;
7738 })
7739 },
7740 }),
7741 metadata: None,
7742 files: USER | PROJECT,
7743 }),
7744 SettingsPageItem::SettingItem(SettingItem {
7745 title: "Preferred Line Length",
7746 description: "The column at which to soft-wrap lines, for buffers where soft-wrap is enabled.",
7747 field: Box::new(SettingField {
7748 json_path: Some("languages.$(language).preferred_line_length"),
7749 pick: |settings_content| {
7750 language_settings_field(settings_content, |language| {
7751 language.preferred_line_length.as_ref()
7752 })
7753 },
7754 write: |settings_content, value| {
7755 language_settings_field_mut(settings_content, value, |language, value| {
7756 language.preferred_line_length = value;
7757 })
7758 },
7759 }),
7760 metadata: None,
7761 files: USER | PROJECT,
7762 }),
7763 SettingsPageItem::SettingItem(SettingItem {
7764 title: "Wrap Guides",
7765 description: "Character counts at which to show wrap guides in the editor.",
7766 field: Box::new(
7767 SettingField {
7768 json_path: Some("languages.$(language).wrap_guides"),
7769 pick: |settings_content| {
7770 language_settings_field(settings_content, |language| {
7771 language.wrap_guides.as_ref()
7772 })
7773 },
7774 write: |settings_content, value| {
7775 language_settings_field_mut(
7776 settings_content,
7777 value,
7778 |language, value| {
7779 language.wrap_guides = value;
7780 },
7781 )
7782 },
7783 }
7784 .unimplemented(),
7785 ),
7786 metadata: None,
7787 files: USER | PROJECT,
7788 }),
7789 SettingsPageItem::SettingItem(SettingItem {
7790 title: "Allow Rewrap",
7791 description: "Controls where the `editor::rewrap` action is allowed for this language.",
7792 field: Box::new(SettingField {
7793 json_path: Some("languages.$(language).allow_rewrap"),
7794 pick: |settings_content| {
7795 language_settings_field(settings_content, |language| {
7796 language.allow_rewrap.as_ref()
7797 })
7798 },
7799 write: |settings_content, value| {
7800 language_settings_field_mut(settings_content, value, |language, value| {
7801 language.allow_rewrap = value;
7802 })
7803 },
7804 }),
7805 metadata: None,
7806 files: USER | PROJECT,
7807 }),
7808 ]
7809 }
7810
7811 fn indent_guides_section() -> [SettingsPageItem; 6] {
7812 [
7813 SettingsPageItem::SectionHeader("Indent Guides"),
7814 SettingsPageItem::SettingItem(SettingItem {
7815 title: "Enabled",
7816 description: "Display indent guides in the editor.",
7817 field: Box::new(SettingField {
7818 json_path: Some("languages.$(language).indent_guides.enabled"),
7819 pick: |settings_content| {
7820 language_settings_field(settings_content, |language| {
7821 language
7822 .indent_guides
7823 .as_ref()
7824 .and_then(|indent_guides| indent_guides.enabled.as_ref())
7825 })
7826 },
7827 write: |settings_content, value| {
7828 language_settings_field_mut(settings_content, value, |language, value| {
7829 language.indent_guides.get_or_insert_default().enabled = value;
7830 })
7831 },
7832 }),
7833 metadata: None,
7834 files: USER | PROJECT,
7835 }),
7836 SettingsPageItem::SettingItem(SettingItem {
7837 title: "Line Width",
7838 description: "The width of the indent guides in pixels, between 1 and 10.",
7839 field: Box::new(SettingField {
7840 json_path: Some("languages.$(language).indent_guides.line_width"),
7841 pick: |settings_content| {
7842 language_settings_field(settings_content, |language| {
7843 language
7844 .indent_guides
7845 .as_ref()
7846 .and_then(|indent_guides| indent_guides.line_width.as_ref())
7847 })
7848 },
7849 write: |settings_content, value| {
7850 language_settings_field_mut(settings_content, value, |language, value| {
7851 language.indent_guides.get_or_insert_default().line_width = value;
7852 })
7853 },
7854 }),
7855 metadata: None,
7856 files: USER | PROJECT,
7857 }),
7858 SettingsPageItem::SettingItem(SettingItem {
7859 title: "Active Line Width",
7860 description: "The width of the active indent guide in pixels, between 1 and 10.",
7861 field: Box::new(SettingField {
7862 json_path: Some("languages.$(language).indent_guides.active_line_width"),
7863 pick: |settings_content| {
7864 language_settings_field(settings_content, |language| {
7865 language
7866 .indent_guides
7867 .as_ref()
7868 .and_then(|indent_guides| indent_guides.active_line_width.as_ref())
7869 })
7870 },
7871 write: |settings_content, value| {
7872 language_settings_field_mut(settings_content, value, |language, value| {
7873 language
7874 .indent_guides
7875 .get_or_insert_default()
7876 .active_line_width = value;
7877 })
7878 },
7879 }),
7880 metadata: None,
7881 files: USER | PROJECT,
7882 }),
7883 SettingsPageItem::SettingItem(SettingItem {
7884 title: "Coloring",
7885 description: "Determines how indent guides are colored.",
7886 field: Box::new(SettingField {
7887 json_path: Some("languages.$(language).indent_guides.coloring"),
7888 pick: |settings_content| {
7889 language_settings_field(settings_content, |language| {
7890 language
7891 .indent_guides
7892 .as_ref()
7893 .and_then(|indent_guides| indent_guides.coloring.as_ref())
7894 })
7895 },
7896 write: |settings_content, value| {
7897 language_settings_field_mut(settings_content, value, |language, value| {
7898 language.indent_guides.get_or_insert_default().coloring = value;
7899 })
7900 },
7901 }),
7902 metadata: None,
7903 files: USER | PROJECT,
7904 }),
7905 SettingsPageItem::SettingItem(SettingItem {
7906 title: "Background Coloring",
7907 description: "Determines how indent guide backgrounds are colored.",
7908 field: Box::new(SettingField {
7909 json_path: Some("languages.$(language).indent_guides.background_coloring"),
7910 pick: |settings_content| {
7911 language_settings_field(settings_content, |language| {
7912 language.indent_guides.as_ref().and_then(|indent_guides| {
7913 indent_guides.background_coloring.as_ref()
7914 })
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 .background_coloring = value;
7923 })
7924 },
7925 }),
7926 metadata: None,
7927 files: USER | PROJECT,
7928 }),
7929 ]
7930 }
7931
7932 fn formatting_section() -> [SettingsPageItem; 7] {
7933 [
7934 SettingsPageItem::SectionHeader("Formatting"),
7935 SettingsPageItem::SettingItem(SettingItem {
7936 title: "Format On Save",
7937 description: "Whether or not to perform a buffer format before saving.",
7938 field: Box::new(
7939 // TODO(settings_ui): this setting should just be a bool
7940 SettingField {
7941 json_path: Some("languages.$(language).format_on_save"),
7942 pick: |settings_content| {
7943 language_settings_field(settings_content, |language| {
7944 language.format_on_save.as_ref()
7945 })
7946 },
7947 write: |settings_content, value| {
7948 language_settings_field_mut(
7949 settings_content,
7950 value,
7951 |language, value| {
7952 language.format_on_save = value;
7953 },
7954 )
7955 },
7956 },
7957 ),
7958 metadata: None,
7959 files: USER | PROJECT,
7960 }),
7961 SettingsPageItem::SettingItem(SettingItem {
7962 title: "Remove Trailing Whitespace On Save",
7963 description: "Whether or not to remove any trailing whitespace from lines of a buffer before saving it.",
7964 field: Box::new(SettingField {
7965 json_path: Some("languages.$(language).remove_trailing_whitespace_on_save"),
7966 pick: |settings_content| {
7967 language_settings_field(settings_content, |language| {
7968 language.remove_trailing_whitespace_on_save.as_ref()
7969 })
7970 },
7971 write: |settings_content, value| {
7972 language_settings_field_mut(settings_content, value, |language, value| {
7973 language.remove_trailing_whitespace_on_save = value;
7974 })
7975 },
7976 }),
7977 metadata: None,
7978 files: USER | PROJECT,
7979 }),
7980 SettingsPageItem::SettingItem(SettingItem {
7981 title: "Ensure Final Newline On Save",
7982 description: "Whether or not to ensure there's a single newline at the end of a buffer when saving it.",
7983 field: Box::new(SettingField {
7984 json_path: Some("languages.$(language).ensure_final_newline_on_save"),
7985 pick: |settings_content| {
7986 language_settings_field(settings_content, |language| {
7987 language.ensure_final_newline_on_save.as_ref()
7988 })
7989 },
7990 write: |settings_content, value| {
7991 language_settings_field_mut(settings_content, value, |language, value| {
7992 language.ensure_final_newline_on_save = value;
7993 })
7994 },
7995 }),
7996 metadata: None,
7997 files: USER | PROJECT,
7998 }),
7999 SettingsPageItem::SettingItem(SettingItem {
8000 title: "Formatter",
8001 description: "How to perform a buffer format.",
8002 field: Box::new(
8003 SettingField {
8004 json_path: Some("languages.$(language).formatter"),
8005 pick: |settings_content| {
8006 language_settings_field(settings_content, |language| {
8007 language.formatter.as_ref()
8008 })
8009 },
8010 write: |settings_content, value| {
8011 language_settings_field_mut(
8012 settings_content,
8013 value,
8014 |language, value| {
8015 language.formatter = value;
8016 },
8017 )
8018 },
8019 }
8020 .unimplemented(),
8021 ),
8022 metadata: None,
8023 files: USER | PROJECT,
8024 }),
8025 SettingsPageItem::SettingItem(SettingItem {
8026 title: "Use On Type Format",
8027 description: "Whether to use additional LSP queries to format (and amend) the code after every \"trigger\" symbol input, defined by LSP server capabilities",
8028 field: Box::new(SettingField {
8029 json_path: Some("languages.$(language).use_on_type_format"),
8030 pick: |settings_content| {
8031 language_settings_field(settings_content, |language| {
8032 language.use_on_type_format.as_ref()
8033 })
8034 },
8035 write: |settings_content, value| {
8036 language_settings_field_mut(settings_content, value, |language, value| {
8037 language.use_on_type_format = value;
8038 })
8039 },
8040 }),
8041 metadata: None,
8042 files: USER | PROJECT,
8043 }),
8044 SettingsPageItem::SettingItem(SettingItem {
8045 title: "Code Actions On Format",
8046 description: "Additional code actions to run when formatting.",
8047 field: Box::new(
8048 SettingField {
8049 json_path: Some("languages.$(language).code_actions_on_format"),
8050 pick: |settings_content| {
8051 language_settings_field(settings_content, |language| {
8052 language.code_actions_on_format.as_ref()
8053 })
8054 },
8055 write: |settings_content, value| {
8056 language_settings_field_mut(
8057 settings_content,
8058 value,
8059 |language, value| {
8060 language.code_actions_on_format = value;
8061 },
8062 )
8063 },
8064 }
8065 .unimplemented(),
8066 ),
8067 metadata: None,
8068 files: USER | PROJECT,
8069 }),
8070 ]
8071 }
8072
8073 fn autoclose_section() -> [SettingsPageItem; 5] {
8074 [
8075 SettingsPageItem::SectionHeader("Autoclose"),
8076 SettingsPageItem::SettingItem(SettingItem {
8077 title: "Use Autoclose",
8078 description: "Whether to automatically type closing characters for you. For example, when you type '(', Zed will automatically add a closing ')' at the correct position.",
8079 field: Box::new(SettingField {
8080 json_path: Some("languages.$(language).use_autoclose"),
8081 pick: |settings_content| {
8082 language_settings_field(settings_content, |language| {
8083 language.use_autoclose.as_ref()
8084 })
8085 },
8086 write: |settings_content, value| {
8087 language_settings_field_mut(settings_content, value, |language, value| {
8088 language.use_autoclose = value;
8089 })
8090 },
8091 }),
8092 metadata: None,
8093 files: USER | PROJECT,
8094 }),
8095 SettingsPageItem::SettingItem(SettingItem {
8096 title: "Use Auto Surround",
8097 description: "Whether to automatically surround text with characters for you. For example, when you select text and type '(', Zed will automatically surround text with ().",
8098 field: Box::new(SettingField {
8099 json_path: Some("languages.$(language).use_auto_surround"),
8100 pick: |settings_content| {
8101 language_settings_field(settings_content, |language| {
8102 language.use_auto_surround.as_ref()
8103 })
8104 },
8105 write: |settings_content, value| {
8106 language_settings_field_mut(settings_content, value, |language, value| {
8107 language.use_auto_surround = value;
8108 })
8109 },
8110 }),
8111 metadata: None,
8112 files: USER | PROJECT,
8113 }),
8114 SettingsPageItem::SettingItem(SettingItem {
8115 title: "Always Treat Brackets As Autoclosed",
8116 description: "Controls whether the closing characters are always skipped over and auto-removed no matter how they were inserted.",
8117 field: Box::new(SettingField {
8118 json_path: Some("languages.$(language).always_treat_brackets_as_autoclosed"),
8119 pick: |settings_content| {
8120 language_settings_field(settings_content, |language| {
8121 language.always_treat_brackets_as_autoclosed.as_ref()
8122 })
8123 },
8124 write: |settings_content, value| {
8125 language_settings_field_mut(settings_content, value, |language, value| {
8126 language.always_treat_brackets_as_autoclosed = value;
8127 })
8128 },
8129 }),
8130 metadata: None,
8131 files: USER | PROJECT,
8132 }),
8133 SettingsPageItem::SettingItem(SettingItem {
8134 title: "JSX Tag Auto Close",
8135 description: "Whether to automatically close JSX tags.",
8136 field: Box::new(SettingField {
8137 json_path: Some("languages.$(language).jsx_tag_auto_close"),
8138 // TODO(settings_ui): this setting should just be a bool
8139 pick: |settings_content| {
8140 language_settings_field(settings_content, |language| {
8141 language.jsx_tag_auto_close.as_ref()?.enabled.as_ref()
8142 })
8143 },
8144 write: |settings_content, value| {
8145 language_settings_field_mut(settings_content, value, |language, value| {
8146 language.jsx_tag_auto_close.get_or_insert_default().enabled = value;
8147 })
8148 },
8149 }),
8150 metadata: None,
8151 files: USER | PROJECT,
8152 }),
8153 ]
8154 }
8155
8156 fn whitespace_section() -> [SettingsPageItem; 4] {
8157 [
8158 SettingsPageItem::SectionHeader("Whitespace"),
8159 SettingsPageItem::SettingItem(SettingItem {
8160 title: "Show Whitespaces",
8161 description: "Whether to show tabs and spaces in the editor.",
8162 field: Box::new(SettingField {
8163 json_path: Some("languages.$(language).show_whitespaces"),
8164 pick: |settings_content| {
8165 language_settings_field(settings_content, |language| {
8166 language.show_whitespaces.as_ref()
8167 })
8168 },
8169 write: |settings_content, value| {
8170 language_settings_field_mut(settings_content, value, |language, value| {
8171 language.show_whitespaces = value;
8172 })
8173 },
8174 }),
8175 metadata: None,
8176 files: USER | PROJECT,
8177 }),
8178 SettingsPageItem::SettingItem(SettingItem {
8179 title: "Space Whitespace Indicator",
8180 description: "Visible character used to render space characters when show_whitespaces is enabled (default: \"•\")",
8181 field: Box::new(
8182 SettingField {
8183 json_path: Some("languages.$(language).whitespace_map.space"),
8184 pick: |settings_content| {
8185 language_settings_field(settings_content, |language| {
8186 language.whitespace_map.as_ref()?.space.as_ref()
8187 })
8188 },
8189 write: |settings_content, value| {
8190 language_settings_field_mut(
8191 settings_content,
8192 value,
8193 |language, value| {
8194 language.whitespace_map.get_or_insert_default().space = value;
8195 },
8196 )
8197 },
8198 }
8199 .unimplemented(),
8200 ),
8201 metadata: None,
8202 files: USER | PROJECT,
8203 }),
8204 SettingsPageItem::SettingItem(SettingItem {
8205 title: "Tab Whitespace Indicator",
8206 description: "Visible character used to render tab characters when show_whitespaces is enabled (default: \"→\")",
8207 field: Box::new(
8208 SettingField {
8209 json_path: Some("languages.$(language).whitespace_map.tab"),
8210 pick: |settings_content| {
8211 language_settings_field(settings_content, |language| {
8212 language.whitespace_map.as_ref()?.tab.as_ref()
8213 })
8214 },
8215 write: |settings_content, value| {
8216 language_settings_field_mut(
8217 settings_content,
8218 value,
8219 |language, value| {
8220 language.whitespace_map.get_or_insert_default().tab = value;
8221 },
8222 )
8223 },
8224 }
8225 .unimplemented(),
8226 ),
8227 metadata: None,
8228 files: USER | PROJECT,
8229 }),
8230 ]
8231 }
8232
8233 fn completions_section() -> [SettingsPageItem; 7] {
8234 [
8235 SettingsPageItem::SectionHeader("Completions"),
8236 SettingsPageItem::SettingItem(SettingItem {
8237 title: "Show Completions On Input",
8238 description: "Whether to pop the completions menu while typing in an editor without explicitly requesting it.",
8239 field: Box::new(SettingField {
8240 json_path: Some("languages.$(language).show_completions_on_input"),
8241 pick: |settings_content| {
8242 language_settings_field(settings_content, |language| {
8243 language.show_completions_on_input.as_ref()
8244 })
8245 },
8246 write: |settings_content, value| {
8247 language_settings_field_mut(settings_content, value, |language, value| {
8248 language.show_completions_on_input = value;
8249 })
8250 },
8251 }),
8252 metadata: None,
8253 files: USER | PROJECT,
8254 }),
8255 SettingsPageItem::SettingItem(SettingItem {
8256 title: "Show Completion Documentation",
8257 description: "Whether to display inline and alongside documentation for items in the completions menu.",
8258 field: Box::new(SettingField {
8259 json_path: Some("languages.$(language).show_completion_documentation"),
8260 pick: |settings_content| {
8261 language_settings_field(settings_content, |language| {
8262 language.show_completion_documentation.as_ref()
8263 })
8264 },
8265 write: |settings_content, value| {
8266 language_settings_field_mut(settings_content, value, |language, value| {
8267 language.show_completion_documentation = value;
8268 })
8269 },
8270 }),
8271 metadata: None,
8272 files: USER | PROJECT,
8273 }),
8274 SettingsPageItem::SettingItem(SettingItem {
8275 title: "Words",
8276 description: "Controls how words are completed.",
8277 field: Box::new(SettingField {
8278 json_path: Some("languages.$(language).completions.words"),
8279 pick: |settings_content| {
8280 language_settings_field(settings_content, |language| {
8281 language.completions.as_ref()?.words.as_ref()
8282 })
8283 },
8284 write: |settings_content, value| {
8285 language_settings_field_mut(settings_content, value, |language, value| {
8286 language.completions.get_or_insert_default().words = value;
8287 })
8288 },
8289 }),
8290 metadata: None,
8291 files: USER | PROJECT,
8292 }),
8293 SettingsPageItem::SettingItem(SettingItem {
8294 title: "Words Min Length",
8295 description: "How many characters has to be in the completions query to automatically show the words-based completions.",
8296 field: Box::new(SettingField {
8297 json_path: Some("languages.$(language).completions.words_min_length"),
8298 pick: |settings_content| {
8299 language_settings_field(settings_content, |language| {
8300 language.completions.as_ref()?.words_min_length.as_ref()
8301 })
8302 },
8303 write: |settings_content, value| {
8304 language_settings_field_mut(settings_content, value, |language, value| {
8305 language
8306 .completions
8307 .get_or_insert_default()
8308 .words_min_length = value;
8309 })
8310 },
8311 }),
8312 metadata: None,
8313 files: USER | PROJECT,
8314 }),
8315 SettingsPageItem::SettingItem(SettingItem {
8316 title: "Completion Menu Scrollbar",
8317 description: "When to show the scrollbar in the completion menu.",
8318 field: Box::new(SettingField {
8319 json_path: Some("editor.completion_menu_scrollbar"),
8320 pick: |settings_content| {
8321 settings_content.editor.completion_menu_scrollbar.as_ref()
8322 },
8323 write: |settings_content, value| {
8324 settings_content.editor.completion_menu_scrollbar = value;
8325 },
8326 }),
8327 metadata: None,
8328 files: USER,
8329 }),
8330 SettingsPageItem::SettingItem(SettingItem {
8331 title: "Completion Detail Alignment",
8332 description: "Whether to align detail text in code completions context menus left or right.",
8333 field: Box::new(SettingField {
8334 json_path: Some("editor.completion_detail_alignment"),
8335 pick: |settings_content| {
8336 settings_content.editor.completion_detail_alignment.as_ref()
8337 },
8338 write: |settings_content, value| {
8339 settings_content.editor.completion_detail_alignment = value;
8340 },
8341 }),
8342 metadata: None,
8343 files: USER,
8344 }),
8345 ]
8346 }
8347
8348 fn inlay_hints_section() -> [SettingsPageItem; 10] {
8349 [
8350 SettingsPageItem::SectionHeader("Inlay Hints"),
8351 SettingsPageItem::SettingItem(SettingItem {
8352 title: "Enabled",
8353 description: "Global switch to toggle hints on and off.",
8354 field: Box::new(SettingField {
8355 json_path: Some("languages.$(language).inlay_hints.enabled"),
8356 pick: |settings_content| {
8357 language_settings_field(settings_content, |language| {
8358 language.inlay_hints.as_ref()?.enabled.as_ref()
8359 })
8360 },
8361 write: |settings_content, value| {
8362 language_settings_field_mut(settings_content, value, |language, value| {
8363 language.inlay_hints.get_or_insert_default().enabled = value;
8364 })
8365 },
8366 }),
8367 metadata: None,
8368 files: USER | PROJECT,
8369 }),
8370 SettingsPageItem::SettingItem(SettingItem {
8371 title: "Show Value Hints",
8372 description: "Global switch to toggle inline values on and off when debugging.",
8373 field: Box::new(SettingField {
8374 json_path: Some("languages.$(language).inlay_hints.show_value_hints"),
8375 pick: |settings_content| {
8376 language_settings_field(settings_content, |language| {
8377 language.inlay_hints.as_ref()?.show_value_hints.as_ref()
8378 })
8379 },
8380 write: |settings_content, value| {
8381 language_settings_field_mut(settings_content, value, |language, value| {
8382 language
8383 .inlay_hints
8384 .get_or_insert_default()
8385 .show_value_hints = value;
8386 })
8387 },
8388 }),
8389 metadata: None,
8390 files: USER | PROJECT,
8391 }),
8392 SettingsPageItem::SettingItem(SettingItem {
8393 title: "Show Type Hints",
8394 description: "Whether type hints should be shown.",
8395 field: Box::new(SettingField {
8396 json_path: Some("languages.$(language).inlay_hints.show_type_hints"),
8397 pick: |settings_content| {
8398 language_settings_field(settings_content, |language| {
8399 language.inlay_hints.as_ref()?.show_type_hints.as_ref()
8400 })
8401 },
8402 write: |settings_content, value| {
8403 language_settings_field_mut(settings_content, value, |language, value| {
8404 language.inlay_hints.get_or_insert_default().show_type_hints = value;
8405 })
8406 },
8407 }),
8408 metadata: None,
8409 files: USER | PROJECT,
8410 }),
8411 SettingsPageItem::SettingItem(SettingItem {
8412 title: "Show Parameter Hints",
8413 description: "Whether parameter hints should be shown.",
8414 field: Box::new(SettingField {
8415 json_path: Some("languages.$(language).inlay_hints.show_parameter_hints"),
8416 pick: |settings_content| {
8417 language_settings_field(settings_content, |language| {
8418 language.inlay_hints.as_ref()?.show_parameter_hints.as_ref()
8419 })
8420 },
8421 write: |settings_content, value| {
8422 language_settings_field_mut(settings_content, value, |language, value| {
8423 language
8424 .inlay_hints
8425 .get_or_insert_default()
8426 .show_parameter_hints = value;
8427 })
8428 },
8429 }),
8430 metadata: None,
8431 files: USER | PROJECT,
8432 }),
8433 SettingsPageItem::SettingItem(SettingItem {
8434 title: "Show Other Hints",
8435 description: "Whether other hints should be shown.",
8436 field: Box::new(SettingField {
8437 json_path: Some("languages.$(language).inlay_hints.show_other_hints"),
8438 pick: |settings_content| {
8439 language_settings_field(settings_content, |language| {
8440 language.inlay_hints.as_ref()?.show_other_hints.as_ref()
8441 })
8442 },
8443 write: |settings_content, value| {
8444 language_settings_field_mut(settings_content, value, |language, value| {
8445 language
8446 .inlay_hints
8447 .get_or_insert_default()
8448 .show_other_hints = value;
8449 })
8450 },
8451 }),
8452 metadata: None,
8453 files: USER | PROJECT,
8454 }),
8455 SettingsPageItem::SettingItem(SettingItem {
8456 title: "Show Background",
8457 description: "Show a background for inlay hints.",
8458 field: Box::new(SettingField {
8459 json_path: Some("languages.$(language).inlay_hints.show_background"),
8460 pick: |settings_content| {
8461 language_settings_field(settings_content, |language| {
8462 language.inlay_hints.as_ref()?.show_background.as_ref()
8463 })
8464 },
8465 write: |settings_content, value| {
8466 language_settings_field_mut(settings_content, value, |language, value| {
8467 language.inlay_hints.get_or_insert_default().show_background = value;
8468 })
8469 },
8470 }),
8471 metadata: None,
8472 files: USER | PROJECT,
8473 }),
8474 SettingsPageItem::SettingItem(SettingItem {
8475 title: "Edit Debounce Ms",
8476 description: "Whether or not to debounce inlay hints updates after buffer edits (set to 0 to disable debouncing).",
8477 field: Box::new(SettingField {
8478 json_path: Some("languages.$(language).inlay_hints.edit_debounce_ms"),
8479 pick: |settings_content| {
8480 language_settings_field(settings_content, |language| {
8481 language.inlay_hints.as_ref()?.edit_debounce_ms.as_ref()
8482 })
8483 },
8484 write: |settings_content, value| {
8485 language_settings_field_mut(settings_content, value, |language, value| {
8486 language
8487 .inlay_hints
8488 .get_or_insert_default()
8489 .edit_debounce_ms = value;
8490 })
8491 },
8492 }),
8493 metadata: None,
8494 files: USER | PROJECT,
8495 }),
8496 SettingsPageItem::SettingItem(SettingItem {
8497 title: "Scroll Debounce Ms",
8498 description: "Whether or not to debounce inlay hints updates after buffer scrolls (set to 0 to disable debouncing).",
8499 field: Box::new(SettingField {
8500 json_path: Some("languages.$(language).inlay_hints.scroll_debounce_ms"),
8501 pick: |settings_content| {
8502 language_settings_field(settings_content, |language| {
8503 language.inlay_hints.as_ref()?.scroll_debounce_ms.as_ref()
8504 })
8505 },
8506 write: |settings_content, value| {
8507 language_settings_field_mut(settings_content, value, |language, value| {
8508 language
8509 .inlay_hints
8510 .get_or_insert_default()
8511 .scroll_debounce_ms = value;
8512 })
8513 },
8514 }),
8515 metadata: None,
8516 files: USER | PROJECT,
8517 }),
8518 SettingsPageItem::SettingItem(SettingItem {
8519 title: "Toggle On Modifiers Press",
8520 description: "Toggles inlay hints (hides or shows) when the user presses the modifiers specified.",
8521 field: Box::new(
8522 SettingField {
8523 json_path: Some(
8524 "languages.$(language).inlay_hints.toggle_on_modifiers_press",
8525 ),
8526 pick: |settings_content| {
8527 language_settings_field(settings_content, |language| {
8528 language
8529 .inlay_hints
8530 .as_ref()?
8531 .toggle_on_modifiers_press
8532 .as_ref()
8533 })
8534 },
8535 write: |settings_content, value| {
8536 language_settings_field_mut(
8537 settings_content,
8538 value,
8539 |language, value| {
8540 language
8541 .inlay_hints
8542 .get_or_insert_default()
8543 .toggle_on_modifiers_press = value;
8544 },
8545 )
8546 },
8547 }
8548 .unimplemented(),
8549 ),
8550 metadata: None,
8551 files: USER | PROJECT,
8552 }),
8553 ]
8554 }
8555
8556 fn tasks_section() -> [SettingsPageItem; 4] {
8557 [
8558 SettingsPageItem::SectionHeader("Tasks"),
8559 SettingsPageItem::SettingItem(SettingItem {
8560 title: "Enabled",
8561 description: "Whether tasks are enabled for this language.",
8562 field: Box::new(SettingField {
8563 json_path: Some("languages.$(language).tasks.enabled"),
8564 pick: |settings_content| {
8565 language_settings_field(settings_content, |language| {
8566 language.tasks.as_ref()?.enabled.as_ref()
8567 })
8568 },
8569 write: |settings_content, value| {
8570 language_settings_field_mut(settings_content, value, |language, value| {
8571 language.tasks.get_or_insert_default().enabled = value;
8572 })
8573 },
8574 }),
8575 metadata: None,
8576 files: USER | PROJECT,
8577 }),
8578 SettingsPageItem::SettingItem(SettingItem {
8579 title: "Variables",
8580 description: "Extra task variables to set for a particular language.",
8581 field: Box::new(
8582 SettingField {
8583 json_path: Some("languages.$(language).tasks.variables"),
8584 pick: |settings_content| {
8585 language_settings_field(settings_content, |language| {
8586 language.tasks.as_ref()?.variables.as_ref()
8587 })
8588 },
8589 write: |settings_content, value| {
8590 language_settings_field_mut(
8591 settings_content,
8592 value,
8593 |language, value| {
8594 language.tasks.get_or_insert_default().variables = value;
8595 },
8596 )
8597 },
8598 }
8599 .unimplemented(),
8600 ),
8601 metadata: None,
8602 files: USER | PROJECT,
8603 }),
8604 SettingsPageItem::SettingItem(SettingItem {
8605 title: "Prefer LSP",
8606 description: "Use LSP tasks over Zed language extension tasks.",
8607 field: Box::new(SettingField {
8608 json_path: Some("languages.$(language).tasks.prefer_lsp"),
8609 pick: |settings_content| {
8610 language_settings_field(settings_content, |language| {
8611 language.tasks.as_ref()?.prefer_lsp.as_ref()
8612 })
8613 },
8614 write: |settings_content, value| {
8615 language_settings_field_mut(settings_content, value, |language, value| {
8616 language.tasks.get_or_insert_default().prefer_lsp = value;
8617 })
8618 },
8619 }),
8620 metadata: None,
8621 files: USER | PROJECT,
8622 }),
8623 ]
8624 }
8625
8626 fn miscellaneous_section() -> [SettingsPageItem; 7] {
8627 [
8628 SettingsPageItem::SectionHeader("Miscellaneous"),
8629 SettingsPageItem::SettingItem(SettingItem {
8630 title: "Word Diff Enabled",
8631 description: "Whether to enable word diff highlighting in the editor. When enabled, changed words within modified lines are highlighted to show exactly what changed.",
8632 field: Box::new(SettingField {
8633 json_path: Some("languages.$(language).word_diff_enabled"),
8634 pick: |settings_content| {
8635 language_settings_field(settings_content, |language| {
8636 language.word_diff_enabled.as_ref()
8637 })
8638 },
8639 write: |settings_content, value| {
8640 language_settings_field_mut(settings_content, value, |language, value| {
8641 language.word_diff_enabled = value;
8642 })
8643 },
8644 }),
8645 metadata: None,
8646 files: USER | PROJECT,
8647 }),
8648 SettingsPageItem::SettingItem(SettingItem {
8649 title: "Debuggers",
8650 description: "Preferred debuggers for this language.",
8651 field: Box::new(
8652 SettingField {
8653 json_path: Some("languages.$(language).debuggers"),
8654 pick: |settings_content| {
8655 language_settings_field(settings_content, |language| {
8656 language.debuggers.as_ref()
8657 })
8658 },
8659 write: |settings_content, value| {
8660 language_settings_field_mut(
8661 settings_content,
8662 value,
8663 |language, value| {
8664 language.debuggers = value;
8665 },
8666 )
8667 },
8668 }
8669 .unimplemented(),
8670 ),
8671 metadata: None,
8672 files: USER | PROJECT,
8673 }),
8674 SettingsPageItem::SettingItem(SettingItem {
8675 title: "Middle Click Paste",
8676 description: "Enable middle-click paste on Linux.",
8677 field: Box::new(SettingField {
8678 json_path: Some("languages.$(language).editor.middle_click_paste"),
8679 pick: |settings_content| settings_content.editor.middle_click_paste.as_ref(),
8680 write: |settings_content, value| {
8681 settings_content.editor.middle_click_paste = value;
8682 },
8683 }),
8684 metadata: None,
8685 files: USER,
8686 }),
8687 SettingsPageItem::SettingItem(SettingItem {
8688 title: "Extend Comment On Newline",
8689 description: "Whether to start a new line with a comment when a previous line is a comment as well.",
8690 field: Box::new(SettingField {
8691 json_path: Some("languages.$(language).extend_comment_on_newline"),
8692 pick: |settings_content| {
8693 language_settings_field(settings_content, |language| {
8694 language.extend_comment_on_newline.as_ref()
8695 })
8696 },
8697 write: |settings_content, value| {
8698 language_settings_field_mut(settings_content, value, |language, value| {
8699 language.extend_comment_on_newline = value;
8700 })
8701 },
8702 }),
8703 metadata: None,
8704 files: USER | PROJECT,
8705 }),
8706 SettingsPageItem::SettingItem(SettingItem {
8707 title: "Colorize Brackets",
8708 description: "Whether to colorize brackets in the editor.",
8709 field: Box::new(SettingField {
8710 json_path: Some("languages.$(language).colorize_brackets"),
8711 pick: |settings_content| {
8712 language_settings_field(settings_content, |language| {
8713 language.colorize_brackets.as_ref()
8714 })
8715 },
8716 write: |settings_content, value| {
8717 language_settings_field_mut(settings_content, value, |language, value| {
8718 language.colorize_brackets = value;
8719 })
8720 },
8721 }),
8722 metadata: None,
8723 files: USER | PROJECT,
8724 }),
8725 SettingsPageItem::SettingItem(SettingItem {
8726 title: "Vim/Emacs Modeline Support",
8727 description: "Number of lines to search for modelines (set to 0 to disable).",
8728 field: Box::new(SettingField {
8729 json_path: Some("modeline_lines"),
8730 pick: |settings_content| settings_content.modeline_lines.as_ref(),
8731 write: |settings_content, value| {
8732 settings_content.modeline_lines = value;
8733 },
8734 }),
8735 metadata: None,
8736 files: USER | PROJECT,
8737 }),
8738 ]
8739 }
8740
8741 fn global_only_miscellaneous_sub_section() -> [SettingsPageItem; 3] {
8742 [
8743 SettingsPageItem::SettingItem(SettingItem {
8744 title: "Image Viewer",
8745 description: "The unit for image file sizes.",
8746 field: Box::new(SettingField {
8747 json_path: Some("image_viewer.unit"),
8748 pick: |settings_content| {
8749 settings_content
8750 .image_viewer
8751 .as_ref()
8752 .and_then(|image_viewer| image_viewer.unit.as_ref())
8753 },
8754 write: |settings_content, value| {
8755 settings_content.image_viewer.get_or_insert_default().unit = value;
8756 },
8757 }),
8758 metadata: None,
8759 files: USER,
8760 }),
8761 SettingsPageItem::SettingItem(SettingItem {
8762 title: "Auto Replace Emoji Shortcode",
8763 description: "Whether to automatically replace emoji shortcodes with emoji characters.",
8764 field: Box::new(SettingField {
8765 json_path: Some("message_editor.auto_replace_emoji_shortcode"),
8766 pick: |settings_content| {
8767 settings_content
8768 .message_editor
8769 .as_ref()
8770 .and_then(|message_editor| {
8771 message_editor.auto_replace_emoji_shortcode.as_ref()
8772 })
8773 },
8774 write: |settings_content, value| {
8775 settings_content
8776 .message_editor
8777 .get_or_insert_default()
8778 .auto_replace_emoji_shortcode = value;
8779 },
8780 }),
8781 metadata: None,
8782 files: USER,
8783 }),
8784 SettingsPageItem::SettingItem(SettingItem {
8785 title: "Drop Size Target",
8786 description: "Relative size of the drop target in the editor that will open dropped file as a split pane.",
8787 field: Box::new(SettingField {
8788 json_path: Some("drop_target_size"),
8789 pick: |settings_content| settings_content.workspace.drop_target_size.as_ref(),
8790 write: |settings_content, value| {
8791 settings_content.workspace.drop_target_size = value;
8792 },
8793 }),
8794 metadata: None,
8795 files: USER,
8796 }),
8797 ]
8798 }
8799
8800 let is_global = active_language().is_none();
8801
8802 let lsp_document_colors_item = [SettingsPageItem::SettingItem(SettingItem {
8803 title: "LSP Document Colors",
8804 description: "How to render LSP color previews in the editor.",
8805 field: Box::new(SettingField {
8806 json_path: Some("lsp_document_colors"),
8807 pick: |settings_content| settings_content.editor.lsp_document_colors.as_ref(),
8808 write: |settings_content, value| {
8809 settings_content.editor.lsp_document_colors = value;
8810 },
8811 }),
8812 metadata: None,
8813 files: USER,
8814 })];
8815
8816 if is_global {
8817 concat_sections!(
8818 indentation_section(),
8819 wrapping_section(),
8820 indent_guides_section(),
8821 formatting_section(),
8822 autoclose_section(),
8823 whitespace_section(),
8824 completions_section(),
8825 inlay_hints_section(),
8826 lsp_document_colors_item,
8827 tasks_section(),
8828 miscellaneous_section(),
8829 global_only_miscellaneous_sub_section(),
8830 )
8831 } else {
8832 concat_sections!(
8833 indentation_section(),
8834 wrapping_section(),
8835 indent_guides_section(),
8836 formatting_section(),
8837 autoclose_section(),
8838 whitespace_section(),
8839 completions_section(),
8840 inlay_hints_section(),
8841 tasks_section(),
8842 miscellaneous_section(),
8843 )
8844 }
8845}
8846
8847/// LanguageSettings items that should be included in the "Languages & Tools" page
8848/// not the "Editor" page
8849fn non_editor_language_settings_data() -> Box<[SettingsPageItem]> {
8850 fn lsp_section() -> [SettingsPageItem; 8] {
8851 [
8852 SettingsPageItem::SectionHeader("LSP"),
8853 SettingsPageItem::SettingItem(SettingItem {
8854 title: "Enable Language Server",
8855 description: "Whether to use language servers to provide code intelligence.",
8856 field: Box::new(SettingField {
8857 json_path: Some("languages.$(language).enable_language_server"),
8858 pick: |settings_content| {
8859 language_settings_field(settings_content, |language| {
8860 language.enable_language_server.as_ref()
8861 })
8862 },
8863 write: |settings_content, value| {
8864 language_settings_field_mut(settings_content, value, |language, value| {
8865 language.enable_language_server = value;
8866 })
8867 },
8868 }),
8869 metadata: None,
8870 files: USER | PROJECT,
8871 }),
8872 SettingsPageItem::SettingItem(SettingItem {
8873 title: "Language Servers",
8874 description: "The list of language servers to use (or disable) for this language.",
8875 field: Box::new(
8876 SettingField {
8877 json_path: Some("languages.$(language).language_servers"),
8878 pick: |settings_content| {
8879 language_settings_field(settings_content, |language| {
8880 language.language_servers.as_ref()
8881 })
8882 },
8883 write: |settings_content, value| {
8884 language_settings_field_mut(
8885 settings_content,
8886 value,
8887 |language, value| {
8888 language.language_servers = value;
8889 },
8890 )
8891 },
8892 }
8893 .unimplemented(),
8894 ),
8895 metadata: None,
8896 files: USER | PROJECT,
8897 }),
8898 SettingsPageItem::SettingItem(SettingItem {
8899 title: "Linked Edits",
8900 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.",
8901 field: Box::new(SettingField {
8902 json_path: Some("languages.$(language).linked_edits"),
8903 pick: |settings_content| {
8904 language_settings_field(settings_content, |language| {
8905 language.linked_edits.as_ref()
8906 })
8907 },
8908 write: |settings_content, value| {
8909 language_settings_field_mut(settings_content, value, |language, value| {
8910 language.linked_edits = value;
8911 })
8912 },
8913 }),
8914 metadata: None,
8915 files: USER | PROJECT,
8916 }),
8917 SettingsPageItem::SettingItem(SettingItem {
8918 title: "Go To Definition Fallback",
8919 description: "Whether to follow-up empty Go to definition responses from the language server.",
8920 field: Box::new(SettingField {
8921 json_path: Some("go_to_definition_fallback"),
8922 pick: |settings_content| {
8923 settings_content.editor.go_to_definition_fallback.as_ref()
8924 },
8925 write: |settings_content, value| {
8926 settings_content.editor.go_to_definition_fallback = value;
8927 },
8928 }),
8929 metadata: None,
8930 files: USER,
8931 }),
8932 SettingsPageItem::SettingItem(SettingItem {
8933 title: "Semantic Tokens",
8934 description: {
8935 static DESCRIPTION: OnceLock<&'static str> = OnceLock::new();
8936 DESCRIPTION.get_or_init(|| {
8937 SemanticTokens::VARIANTS
8938 .iter()
8939 .filter_map(|v| {
8940 v.get_documentation().map(|doc| format!("{v:?}: {doc}"))
8941 })
8942 .join("\n")
8943 .leak()
8944 })
8945 },
8946 field: Box::new(SettingField {
8947 json_path: Some("languages.$(language).semantic_tokens"),
8948 pick: |settings_content| {
8949 settings_content
8950 .project
8951 .all_languages
8952 .defaults
8953 .semantic_tokens
8954 .as_ref()
8955 },
8956 write: |settings_content, value| {
8957 settings_content
8958 .project
8959 .all_languages
8960 .defaults
8961 .semantic_tokens = value;
8962 },
8963 }),
8964 metadata: None,
8965 files: USER | PROJECT,
8966 }),
8967 SettingsPageItem::SettingItem(SettingItem {
8968 title: "LSP Folding Ranges",
8969 description: "When enabled, use folding ranges from the language server instead of indent-based folding.",
8970 field: Box::new(SettingField {
8971 json_path: Some("languages.$(language).document_folding_ranges"),
8972 pick: |settings_content| {
8973 language_settings_field(settings_content, |language| {
8974 language.document_folding_ranges.as_ref()
8975 })
8976 },
8977 write: |settings_content, value| {
8978 language_settings_field_mut(settings_content, value, |language, value| {
8979 language.document_folding_ranges = value;
8980 })
8981 },
8982 }),
8983 metadata: None,
8984 files: USER | PROJECT,
8985 }),
8986 SettingsPageItem::SettingItem(SettingItem {
8987 title: "LSP Document Symbols",
8988 description: "When enabled, use the language server's document symbols for outlines and breadcrumbs instead of tree-sitter.",
8989 field: Box::new(SettingField {
8990 json_path: Some("languages.$(language).document_symbols"),
8991 pick: |settings_content| {
8992 language_settings_field(settings_content, |language| {
8993 language.document_symbols.as_ref()
8994 })
8995 },
8996 write: |settings_content, value| {
8997 language_settings_field_mut(settings_content, value, |language, value| {
8998 language.document_symbols = value;
8999 })
9000 },
9001 }),
9002 metadata: None,
9003 files: USER | PROJECT,
9004 }),
9005 ]
9006 }
9007
9008 fn lsp_completions_section() -> [SettingsPageItem; 4] {
9009 [
9010 SettingsPageItem::SectionHeader("LSP Completions"),
9011 SettingsPageItem::SettingItem(SettingItem {
9012 title: "Enabled",
9013 description: "Whether to fetch LSP completions or not.",
9014 field: Box::new(SettingField {
9015 json_path: Some("languages.$(language).completions.lsp"),
9016 pick: |settings_content| {
9017 language_settings_field(settings_content, |language| {
9018 language.completions.as_ref()?.lsp.as_ref()
9019 })
9020 },
9021 write: |settings_content, value| {
9022 language_settings_field_mut(settings_content, value, |language, value| {
9023 language.completions.get_or_insert_default().lsp = value;
9024 })
9025 },
9026 }),
9027 metadata: None,
9028 files: USER | PROJECT,
9029 }),
9030 SettingsPageItem::SettingItem(SettingItem {
9031 title: "Fetch Timeout (milliseconds)",
9032 description: "When fetching LSP completions, determines how long to wait for a response of a particular server (set to 0 to wait indefinitely).",
9033 field: Box::new(SettingField {
9034 json_path: Some("languages.$(language).completions.lsp_fetch_timeout_ms"),
9035 pick: |settings_content| {
9036 language_settings_field(settings_content, |language| {
9037 language.completions.as_ref()?.lsp_fetch_timeout_ms.as_ref()
9038 })
9039 },
9040 write: |settings_content, value| {
9041 language_settings_field_mut(settings_content, value, |language, value| {
9042 language
9043 .completions
9044 .get_or_insert_default()
9045 .lsp_fetch_timeout_ms = value;
9046 })
9047 },
9048 }),
9049 metadata: None,
9050 files: USER | PROJECT,
9051 }),
9052 SettingsPageItem::SettingItem(SettingItem {
9053 title: "Insert Mode",
9054 description: "Controls how LSP completions are inserted.",
9055 field: Box::new(SettingField {
9056 json_path: Some("languages.$(language).completions.lsp_insert_mode"),
9057 pick: |settings_content| {
9058 language_settings_field(settings_content, |language| {
9059 language.completions.as_ref()?.lsp_insert_mode.as_ref()
9060 })
9061 },
9062 write: |settings_content, value| {
9063 language_settings_field_mut(settings_content, value, |language, value| {
9064 language.completions.get_or_insert_default().lsp_insert_mode = value;
9065 })
9066 },
9067 }),
9068 metadata: None,
9069 files: USER | PROJECT,
9070 }),
9071 ]
9072 }
9073
9074 fn debugger_section() -> [SettingsPageItem; 2] {
9075 [
9076 SettingsPageItem::SectionHeader("Debuggers"),
9077 SettingsPageItem::SettingItem(SettingItem {
9078 title: "Debuggers",
9079 description: "Preferred debuggers for this language.",
9080 field: Box::new(
9081 SettingField {
9082 json_path: Some("languages.$(language).debuggers"),
9083 pick: |settings_content| {
9084 language_settings_field(settings_content, |language| {
9085 language.debuggers.as_ref()
9086 })
9087 },
9088 write: |settings_content, value| {
9089 language_settings_field_mut(
9090 settings_content,
9091 value,
9092 |language, value| {
9093 language.debuggers = value;
9094 },
9095 )
9096 },
9097 }
9098 .unimplemented(),
9099 ),
9100 metadata: None,
9101 files: USER | PROJECT,
9102 }),
9103 ]
9104 }
9105
9106 fn prettier_section() -> [SettingsPageItem; 5] {
9107 [
9108 SettingsPageItem::SectionHeader("Prettier"),
9109 SettingsPageItem::SettingItem(SettingItem {
9110 title: "Allowed",
9111 description: "Enables or disables formatting with Prettier for a given language.",
9112 field: Box::new(SettingField {
9113 json_path: Some("languages.$(language).prettier.allowed"),
9114 pick: |settings_content| {
9115 language_settings_field(settings_content, |language| {
9116 language.prettier.as_ref()?.allowed.as_ref()
9117 })
9118 },
9119 write: |settings_content, value| {
9120 language_settings_field_mut(settings_content, value, |language, value| {
9121 language.prettier.get_or_insert_default().allowed = value;
9122 })
9123 },
9124 }),
9125 metadata: None,
9126 files: USER | PROJECT,
9127 }),
9128 SettingsPageItem::SettingItem(SettingItem {
9129 title: "Parser",
9130 description: "Forces Prettier integration to use a specific parser name when formatting files with the language.",
9131 field: Box::new(SettingField {
9132 json_path: Some("languages.$(language).prettier.parser"),
9133 pick: |settings_content| {
9134 language_settings_field(settings_content, |language| {
9135 language.prettier.as_ref()?.parser.as_ref()
9136 })
9137 },
9138 write: |settings_content, value| {
9139 language_settings_field_mut(settings_content, value, |language, value| {
9140 language.prettier.get_or_insert_default().parser = value;
9141 })
9142 },
9143 }),
9144 metadata: None,
9145 files: USER | PROJECT,
9146 }),
9147 SettingsPageItem::SettingItem(SettingItem {
9148 title: "Plugins",
9149 description: "Forces Prettier integration to use specific plugins when formatting files with the language.",
9150 field: Box::new(
9151 SettingField {
9152 json_path: Some("languages.$(language).prettier.plugins"),
9153 pick: |settings_content| {
9154 language_settings_field(settings_content, |language| {
9155 language.prettier.as_ref()?.plugins.as_ref()
9156 })
9157 },
9158 write: |settings_content, value| {
9159 language_settings_field_mut(
9160 settings_content,
9161 value,
9162 |language, value| {
9163 language.prettier.get_or_insert_default().plugins = value;
9164 },
9165 )
9166 },
9167 }
9168 .unimplemented(),
9169 ),
9170 metadata: None,
9171 files: USER | PROJECT,
9172 }),
9173 SettingsPageItem::SettingItem(SettingItem {
9174 title: "Options",
9175 description: "Default Prettier options, in the format as in package.json section for Prettier.",
9176 field: Box::new(
9177 SettingField {
9178 json_path: Some("languages.$(language).prettier.options"),
9179 pick: |settings_content| {
9180 language_settings_field(settings_content, |language| {
9181 language.prettier.as_ref()?.options.as_ref()
9182 })
9183 },
9184 write: |settings_content, value| {
9185 language_settings_field_mut(
9186 settings_content,
9187 value,
9188 |language, value| {
9189 language.prettier.get_or_insert_default().options = value;
9190 },
9191 )
9192 },
9193 }
9194 .unimplemented(),
9195 ),
9196 metadata: None,
9197 files: USER | PROJECT,
9198 }),
9199 ]
9200 }
9201
9202 concat_sections!(
9203 lsp_section(),
9204 lsp_completions_section(),
9205 debugger_section(),
9206 prettier_section(),
9207 )
9208}
9209
9210fn edit_prediction_language_settings_section() -> [SettingsPageItem; 4] {
9211 [
9212 SettingsPageItem::SectionHeader("Edit Predictions"),
9213 SettingsPageItem::SubPageLink(SubPageLink {
9214 title: "Configure Providers".into(),
9215 r#type: Default::default(),
9216 json_path: Some("edit_predictions.providers"),
9217 description: Some("Set up different edit prediction providers in complement to Zed's built-in Zeta model.".into()),
9218 in_json: false,
9219 files: USER,
9220 render: render_edit_prediction_setup_page
9221 }),
9222 SettingsPageItem::SettingItem(SettingItem {
9223 title: "Show Edit Predictions",
9224 description: "Controls whether edit predictions are shown immediately or manually.",
9225 field: Box::new(SettingField {
9226 json_path: Some("languages.$(language).show_edit_predictions"),
9227 pick: |settings_content| {
9228 language_settings_field(settings_content, |language| {
9229 language.show_edit_predictions.as_ref()
9230 })
9231 },
9232 write: |settings_content, value| {
9233 language_settings_field_mut(settings_content, value, |language, value| {
9234 language.show_edit_predictions = value;
9235 })
9236 },
9237 }),
9238 metadata: None,
9239 files: USER | PROJECT,
9240 }),
9241 SettingsPageItem::SettingItem(SettingItem {
9242 title: "Disable in Language Scopes",
9243 description: "Controls whether edit predictions are shown in the given language scopes.",
9244 field: Box::new(
9245 SettingField {
9246 json_path: Some("languages.$(language).edit_predictions_disabled_in"),
9247 pick: |settings_content| {
9248 language_settings_field(settings_content, |language| {
9249 language.edit_predictions_disabled_in.as_ref()
9250 })
9251 },
9252 write: |settings_content, value| {
9253 language_settings_field_mut(settings_content, value, |language, value| {
9254 language.edit_predictions_disabled_in = value;
9255 })
9256 },
9257 }
9258 .unimplemented(),
9259 ),
9260 metadata: None,
9261 files: USER | PROJECT,
9262 }),
9263 ]
9264}
9265
9266fn show_scrollbar_or_editor(
9267 settings_content: &SettingsContent,
9268 show: fn(&SettingsContent) -> Option<&settings::ShowScrollbar>,
9269) -> Option<&settings::ShowScrollbar> {
9270 show(settings_content).or(settings_content
9271 .editor
9272 .scrollbar
9273 .as_ref()
9274 .and_then(|scrollbar| scrollbar.show.as_ref()))
9275}
9276
9277fn dynamic_variants<T>() -> &'static [T::Discriminant]
9278where
9279 T: strum::IntoDiscriminant,
9280 T::Discriminant: strum::VariantArray,
9281{
9282 <<T as strum::IntoDiscriminant>::Discriminant as strum::VariantArray>::VARIANTS
9283}
9284
9285/// Updates the `vim_mode` setting, disabling `helix_mode` if present and
9286/// `vim_mode` is being enabled.
9287fn write_vim_mode(settings: &mut SettingsContent, value: Option<bool>) {
9288 if value == Some(true) && settings.helix_mode == Some(true) {
9289 settings.helix_mode = Some(false);
9290 }
9291 settings.vim_mode = value;
9292}
9293
9294/// Updates the `helix_mode` setting, disabling `vim_mode` if present and
9295/// `helix_mode` is being enabled.
9296fn write_helix_mode(settings: &mut SettingsContent, value: Option<bool>) {
9297 if value == Some(true) && settings.vim_mode == Some(true) {
9298 settings.vim_mode = Some(false);
9299 }
9300 settings.helix_mode = value;
9301}
9302
9303#[cfg(test)]
9304mod tests {
9305 use super::*;
9306
9307 #[test]
9308 fn test_write_vim_helix_mode() {
9309 // Enabling vim mode while `vim_mode` and `helix_mode` are not yet set
9310 // should only update the `vim_mode` setting.
9311 let mut settings = SettingsContent::default();
9312 write_vim_mode(&mut settings, Some(true));
9313 assert_eq!(settings.vim_mode, Some(true));
9314 assert_eq!(settings.helix_mode, None);
9315
9316 // Enabling helix mode while `vim_mode` and `helix_mode` are not yet set
9317 // should only update the `helix_mode` setting.
9318 let mut settings = SettingsContent::default();
9319 write_helix_mode(&mut settings, Some(true));
9320 assert_eq!(settings.helix_mode, Some(true));
9321 assert_eq!(settings.vim_mode, None);
9322
9323 // Disabling helix mode should only touch `helix_mode` setting when
9324 // `vim_mode` is not set.
9325 write_helix_mode(&mut settings, Some(false));
9326 assert_eq!(settings.helix_mode, Some(false));
9327 assert_eq!(settings.vim_mode, None);
9328
9329 // Enabling vim mode should update `vim_mode` but leave `helix_mode`
9330 // untouched.
9331 write_vim_mode(&mut settings, Some(true));
9332 assert_eq!(settings.vim_mode, Some(true));
9333 assert_eq!(settings.helix_mode, Some(false));
9334
9335 // Enabling helix mode should update `helix_mode` and disable
9336 // `vim_mode`.
9337 write_helix_mode(&mut settings, Some(true));
9338 assert_eq!(settings.helix_mode, Some(true));
9339 assert_eq!(settings.vim_mode, Some(false));
9340
9341 // Enabling vim mode should update `vim_mode` and disable
9342 // `helix_mode`.
9343 write_vim_mode(&mut settings, Some(true));
9344 assert_eq!(settings.vim_mode, Some(true));
9345 assert_eq!(settings.helix_mode, Some(false));
9346 }
9347}