page_data.rs

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