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