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