page_data.rs

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