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