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