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