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