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