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