page_data.rs

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