page_data.rs

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