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