page_data.rs

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