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