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