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