page_data.rs

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