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