page_data.rs

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