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