page_data.rs

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