page_data.rs

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