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; 10] {
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        ]
6777    }
6778
6779    fn context_servers_section() -> [SettingsPageItem; 2] {
6780        [
6781            SettingsPageItem::SectionHeader("Context Servers"),
6782            SettingsPageItem::SettingItem(SettingItem {
6783                title: "Context Server Timeout",
6784                description: "Default timeout in seconds for context server tool calls. Can be overridden per-server in context_servers configuration.",
6785                field: Box::new(SettingField {
6786                    json_path: Some("context_server_timeout"),
6787                    pick: |settings_content| {
6788                        settings_content.project.context_server_timeout.as_ref()
6789                    },
6790                    write: |settings_content, value| {
6791                        settings_content.project.context_server_timeout = value;
6792                    },
6793                }),
6794                metadata: None,
6795                files: USER | PROJECT,
6796            }),
6797        ]
6798    }
6799
6800    fn edit_prediction_display_sub_section() -> [SettingsPageItem; 2] {
6801        [
6802            SettingsPageItem::SettingItem(SettingItem {
6803                title: "Display Mode",
6804                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.",
6805                field: Box::new(SettingField {
6806                    json_path: Some("edit_prediction.display_mode"),
6807                    pick: |settings_content| {
6808                        settings_content
6809                            .project
6810                            .all_languages
6811                            .edit_predictions
6812                            .as_ref()?
6813                            .mode
6814                            .as_ref()
6815                    },
6816                    write: |settings_content, value| {
6817                        settings_content
6818                            .project
6819                            .all_languages
6820                            .edit_predictions
6821                            .get_or_insert_default()
6822                            .mode = value;
6823                    },
6824                }),
6825                metadata: None,
6826                files: USER,
6827            }),
6828            SettingsPageItem::SettingItem(SettingItem {
6829                title: "Display In Text Threads",
6830                description: "Whether edit predictions are enabled when editing text threads in the agent panel.",
6831                field: Box::new(SettingField {
6832                    json_path: Some("edit_prediction.in_text_threads"),
6833                    pick: |settings_content| {
6834                        settings_content
6835                            .project
6836                            .all_languages
6837                            .edit_predictions
6838                            .as_ref()?
6839                            .enabled_in_text_threads
6840                            .as_ref()
6841                    },
6842                    write: |settings_content, value| {
6843                        settings_content
6844                            .project
6845                            .all_languages
6846                            .edit_predictions
6847                            .get_or_insert_default()
6848                            .enabled_in_text_threads = value;
6849                    },
6850                }),
6851                metadata: None,
6852                files: USER,
6853            }),
6854        ]
6855    }
6856
6857    SettingsPage {
6858        title: "AI",
6859        items: concat_sections![
6860            general_section(),
6861            agent_configuration_section(),
6862            context_servers_section(),
6863            edit_prediction_language_settings_section(),
6864            edit_prediction_display_sub_section()
6865        ],
6866    }
6867}
6868
6869fn network_page() -> SettingsPage {
6870    fn network_section() -> [SettingsPageItem; 3] {
6871        [
6872            SettingsPageItem::SectionHeader("Network"),
6873            SettingsPageItem::SettingItem(SettingItem {
6874                title: "Proxy",
6875                description: "The proxy to use for network requests.",
6876                field: Box::new(SettingField {
6877                    json_path: Some("proxy"),
6878                    pick: |settings_content| settings_content.proxy.as_ref(),
6879                    write: |settings_content, value| {
6880                        settings_content.proxy = value;
6881                    },
6882                }),
6883                metadata: Some(Box::new(SettingsFieldMetadata {
6884                    placeholder: Some("socks5h://localhost:10808"),
6885                    ..Default::default()
6886                })),
6887                files: USER,
6888            }),
6889            SettingsPageItem::SettingItem(SettingItem {
6890                title: "Server URL",
6891                description: "The URL of the Zed server to connect to.",
6892                field: Box::new(SettingField {
6893                    json_path: Some("server_url"),
6894                    pick: |settings_content| settings_content.server_url.as_ref(),
6895                    write: |settings_content, value| {
6896                        settings_content.server_url = value;
6897                    },
6898                }),
6899                metadata: Some(Box::new(SettingsFieldMetadata {
6900                    placeholder: Some("https://zed.dev"),
6901                    ..Default::default()
6902                })),
6903                files: USER,
6904            }),
6905        ]
6906    }
6907
6908    SettingsPage {
6909        title: "Network",
6910        items: concat_sections![network_section()],
6911    }
6912}
6913
6914const LANGUAGES_SECTION_HEADER: &'static str = "Languages";
6915
6916fn current_language() -> Option<SharedString> {
6917    sub_page_stack().iter().find_map(|page| {
6918        (page.section_header == LANGUAGES_SECTION_HEADER).then(|| page.link.title.clone())
6919    })
6920}
6921
6922fn language_settings_field<T>(
6923    settings_content: &SettingsContent,
6924    get: fn(&LanguageSettingsContent) -> Option<&T>,
6925) -> Option<&T> {
6926    let all_languages = &settings_content.project.all_languages;
6927    if let Some(current_language_name) = current_language() {
6928        if let Some(current_language) = all_languages.languages.0.get(&current_language_name) {
6929            let value = get(current_language);
6930            if value.is_some() {
6931                return value;
6932            }
6933        }
6934    }
6935    let default_value = get(&all_languages.defaults);
6936    return default_value;
6937}
6938
6939fn language_settings_field_mut<T>(
6940    settings_content: &mut SettingsContent,
6941    value: Option<T>,
6942    write: fn(&mut LanguageSettingsContent, Option<T>),
6943) {
6944    let all_languages = &mut settings_content.project.all_languages;
6945    let language_content = if let Some(current_language) = current_language() {
6946        all_languages
6947            .languages
6948            .0
6949            .entry(current_language)
6950            .or_default()
6951    } else {
6952        &mut all_languages.defaults
6953    };
6954    write(language_content, value);
6955}
6956
6957fn language_settings_data() -> Box<[SettingsPageItem]> {
6958    fn indentation_section() -> [SettingsPageItem; 5] {
6959        [
6960            SettingsPageItem::SectionHeader("Indentation"),
6961            SettingsPageItem::SettingItem(SettingItem {
6962                title: "Tab Size",
6963                description: "How many columns a tab should occupy.",
6964                field: Box::new(SettingField {
6965                    json_path: Some("languages.$(language).tab_size"), // TODO(cameron): not JQ syntax because not URL-safe
6966                    pick: |settings_content| {
6967                        language_settings_field(settings_content, |language| {
6968                            language.tab_size.as_ref()
6969                        })
6970                    },
6971                    write: |settings_content, value| {
6972                        language_settings_field_mut(settings_content, value, |language, value| {
6973                            language.tab_size = value;
6974                        })
6975                    },
6976                }),
6977                metadata: None,
6978                files: USER | PROJECT,
6979            }),
6980            SettingsPageItem::SettingItem(SettingItem {
6981                title: "Hard Tabs",
6982                description: "Whether to indent lines using tab characters, as opposed to multiple spaces.",
6983                field: Box::new(SettingField {
6984                    json_path: Some("languages.$(language).hard_tabs"),
6985                    pick: |settings_content| {
6986                        language_settings_field(settings_content, |language| {
6987                            language.hard_tabs.as_ref()
6988                        })
6989                    },
6990                    write: |settings_content, value| {
6991                        language_settings_field_mut(settings_content, value, |language, value| {
6992                            language.hard_tabs = value;
6993                        })
6994                    },
6995                }),
6996                metadata: None,
6997                files: USER | PROJECT,
6998            }),
6999            SettingsPageItem::SettingItem(SettingItem {
7000                title: "Auto Indent",
7001                description: "Whether indentation should be adjusted based on the context whilst typing.",
7002                field: Box::new(SettingField {
7003                    json_path: Some("languages.$(language).auto_indent"),
7004                    pick: |settings_content| {
7005                        language_settings_field(settings_content, |language| {
7006                            language.auto_indent.as_ref()
7007                        })
7008                    },
7009                    write: |settings_content, value| {
7010                        language_settings_field_mut(settings_content, value, |language, value| {
7011                            language.auto_indent = value;
7012                        })
7013                    },
7014                }),
7015                metadata: None,
7016                files: USER | PROJECT,
7017            }),
7018            SettingsPageItem::SettingItem(SettingItem {
7019                title: "Auto Indent On Paste",
7020                description: "Whether indentation of pasted content should be adjusted based on the context.",
7021                field: Box::new(SettingField {
7022                    json_path: Some("languages.$(language).auto_indent_on_paste"),
7023                    pick: |settings_content| {
7024                        language_settings_field(settings_content, |language| {
7025                            language.auto_indent_on_paste.as_ref()
7026                        })
7027                    },
7028                    write: |settings_content, value| {
7029                        language_settings_field_mut(settings_content, value, |language, value| {
7030                            language.auto_indent_on_paste = value;
7031                        })
7032                    },
7033                }),
7034                metadata: None,
7035                files: USER | PROJECT,
7036            }),
7037        ]
7038    }
7039
7040    fn wrapping_section() -> [SettingsPageItem; 6] {
7041        [
7042            SettingsPageItem::SectionHeader("Wrapping"),
7043            SettingsPageItem::SettingItem(SettingItem {
7044                title: "Soft Wrap",
7045                description: "How to soft-wrap long lines of text.",
7046                field: Box::new(SettingField {
7047                    json_path: Some("languages.$(language).soft_wrap"),
7048                    pick: |settings_content| {
7049                        language_settings_field(settings_content, |language| {
7050                            language.soft_wrap.as_ref()
7051                        })
7052                    },
7053                    write: |settings_content, value| {
7054                        language_settings_field_mut(settings_content, value, |language, value| {
7055                            language.soft_wrap = value;
7056                        })
7057                    },
7058                }),
7059                metadata: None,
7060                files: USER | PROJECT,
7061            }),
7062            SettingsPageItem::SettingItem(SettingItem {
7063                title: "Show Wrap Guides",
7064                description: "Show wrap guides in the editor.",
7065                field: Box::new(SettingField {
7066                    json_path: Some("languages.$(language).show_wrap_guides"),
7067                    pick: |settings_content| {
7068                        language_settings_field(settings_content, |language| {
7069                            language.show_wrap_guides.as_ref()
7070                        })
7071                    },
7072                    write: |settings_content, value| {
7073                        language_settings_field_mut(settings_content, value, |language, value| {
7074                            language.show_wrap_guides = value;
7075                        })
7076                    },
7077                }),
7078                metadata: None,
7079                files: USER | PROJECT,
7080            }),
7081            SettingsPageItem::SettingItem(SettingItem {
7082                title: "Preferred Line Length",
7083                description: "The column at which to soft-wrap lines, for buffers where soft-wrap is enabled.",
7084                field: Box::new(SettingField {
7085                    json_path: Some("languages.$(language).preferred_line_length"),
7086                    pick: |settings_content| {
7087                        language_settings_field(settings_content, |language| {
7088                            language.preferred_line_length.as_ref()
7089                        })
7090                    },
7091                    write: |settings_content, value| {
7092                        language_settings_field_mut(settings_content, value, |language, value| {
7093                            language.preferred_line_length = value;
7094                        })
7095                    },
7096                }),
7097                metadata: None,
7098                files: USER | PROJECT,
7099            }),
7100            SettingsPageItem::SettingItem(SettingItem {
7101                title: "Wrap Guides",
7102                description: "Character counts at which to show wrap guides in the editor.",
7103                field: Box::new(
7104                    SettingField {
7105                        json_path: Some("languages.$(language).wrap_guides"),
7106                        pick: |settings_content| {
7107                            language_settings_field(settings_content, |language| {
7108                                language.wrap_guides.as_ref()
7109                            })
7110                        },
7111                        write: |settings_content, value| {
7112                            language_settings_field_mut(
7113                                settings_content,
7114                                value,
7115                                |language, value| {
7116                                    language.wrap_guides = value;
7117                                },
7118                            )
7119                        },
7120                    }
7121                    .unimplemented(),
7122                ),
7123                metadata: None,
7124                files: USER | PROJECT,
7125            }),
7126            SettingsPageItem::SettingItem(SettingItem {
7127                title: "Allow Rewrap",
7128                description: "Controls where the `editor::rewrap` action is allowed for this language.",
7129                field: Box::new(SettingField {
7130                    json_path: Some("languages.$(language).allow_rewrap"),
7131                    pick: |settings_content| {
7132                        language_settings_field(settings_content, |language| {
7133                            language.allow_rewrap.as_ref()
7134                        })
7135                    },
7136                    write: |settings_content, value| {
7137                        language_settings_field_mut(settings_content, value, |language, value| {
7138                            language.allow_rewrap = value;
7139                        })
7140                    },
7141                }),
7142                metadata: None,
7143                files: USER | PROJECT,
7144            }),
7145        ]
7146    }
7147
7148    fn indent_guides_section() -> [SettingsPageItem; 6] {
7149        [
7150            SettingsPageItem::SectionHeader("Indent Guides"),
7151            SettingsPageItem::SettingItem(SettingItem {
7152                title: "Enabled",
7153                description: "Display indent guides in the editor.",
7154                field: Box::new(SettingField {
7155                    json_path: Some("languages.$(language).indent_guides.enabled"),
7156                    pick: |settings_content| {
7157                        language_settings_field(settings_content, |language| {
7158                            language
7159                                .indent_guides
7160                                .as_ref()
7161                                .and_then(|indent_guides| indent_guides.enabled.as_ref())
7162                        })
7163                    },
7164                    write: |settings_content, value| {
7165                        language_settings_field_mut(settings_content, value, |language, value| {
7166                            language.indent_guides.get_or_insert_default().enabled = value;
7167                        })
7168                    },
7169                }),
7170                metadata: None,
7171                files: USER | PROJECT,
7172            }),
7173            SettingsPageItem::SettingItem(SettingItem {
7174                title: "Line Width",
7175                description: "The width of the indent guides in pixels, between 1 and 10.",
7176                field: Box::new(SettingField {
7177                    json_path: Some("languages.$(language).indent_guides.line_width"),
7178                    pick: |settings_content| {
7179                        language_settings_field(settings_content, |language| {
7180                            language
7181                                .indent_guides
7182                                .as_ref()
7183                                .and_then(|indent_guides| indent_guides.line_width.as_ref())
7184                        })
7185                    },
7186                    write: |settings_content, value| {
7187                        language_settings_field_mut(settings_content, value, |language, value| {
7188                            language.indent_guides.get_or_insert_default().line_width = value;
7189                        })
7190                    },
7191                }),
7192                metadata: None,
7193                files: USER | PROJECT,
7194            }),
7195            SettingsPageItem::SettingItem(SettingItem {
7196                title: "Active Line Width",
7197                description: "The width of the active indent guide in pixels, between 1 and 10.",
7198                field: Box::new(SettingField {
7199                    json_path: Some("languages.$(language).indent_guides.active_line_width"),
7200                    pick: |settings_content| {
7201                        language_settings_field(settings_content, |language| {
7202                            language
7203                                .indent_guides
7204                                .as_ref()
7205                                .and_then(|indent_guides| indent_guides.active_line_width.as_ref())
7206                        })
7207                    },
7208                    write: |settings_content, value| {
7209                        language_settings_field_mut(settings_content, value, |language, value| {
7210                            language
7211                                .indent_guides
7212                                .get_or_insert_default()
7213                                .active_line_width = value;
7214                        })
7215                    },
7216                }),
7217                metadata: None,
7218                files: USER | PROJECT,
7219            }),
7220            SettingsPageItem::SettingItem(SettingItem {
7221                title: "Coloring",
7222                description: "Determines how indent guides are colored.",
7223                field: Box::new(SettingField {
7224                    json_path: Some("languages.$(language).indent_guides.coloring"),
7225                    pick: |settings_content| {
7226                        language_settings_field(settings_content, |language| {
7227                            language
7228                                .indent_guides
7229                                .as_ref()
7230                                .and_then(|indent_guides| indent_guides.coloring.as_ref())
7231                        })
7232                    },
7233                    write: |settings_content, value| {
7234                        language_settings_field_mut(settings_content, value, |language, value| {
7235                            language.indent_guides.get_or_insert_default().coloring = value;
7236                        })
7237                    },
7238                }),
7239                metadata: None,
7240                files: USER | PROJECT,
7241            }),
7242            SettingsPageItem::SettingItem(SettingItem {
7243                title: "Background Coloring",
7244                description: "Determines how indent guide backgrounds are colored.",
7245                field: Box::new(SettingField {
7246                    json_path: Some("languages.$(language).indent_guides.background_coloring"),
7247                    pick: |settings_content| {
7248                        language_settings_field(settings_content, |language| {
7249                            language.indent_guides.as_ref().and_then(|indent_guides| {
7250                                indent_guides.background_coloring.as_ref()
7251                            })
7252                        })
7253                    },
7254                    write: |settings_content, value| {
7255                        language_settings_field_mut(settings_content, value, |language, value| {
7256                            language
7257                                .indent_guides
7258                                .get_or_insert_default()
7259                                .background_coloring = value;
7260                        })
7261                    },
7262                }),
7263                metadata: None,
7264                files: USER | PROJECT,
7265            }),
7266        ]
7267    }
7268
7269    fn formatting_section() -> [SettingsPageItem; 7] {
7270        [
7271            SettingsPageItem::SectionHeader("Formatting"),
7272            SettingsPageItem::SettingItem(SettingItem {
7273                title: "Format On Save",
7274                description: "Whether or not to perform a buffer format before saving.",
7275                field: Box::new(
7276                    // TODO(settings_ui): this setting should just be a bool
7277                    SettingField {
7278                        json_path: Some("languages.$(language).format_on_save"),
7279                        pick: |settings_content| {
7280                            language_settings_field(settings_content, |language| {
7281                                language.format_on_save.as_ref()
7282                            })
7283                        },
7284                        write: |settings_content, value| {
7285                            language_settings_field_mut(
7286                                settings_content,
7287                                value,
7288                                |language, value| {
7289                                    language.format_on_save = value;
7290                                },
7291                            )
7292                        },
7293                    },
7294                ),
7295                metadata: None,
7296                files: USER | PROJECT,
7297            }),
7298            SettingsPageItem::SettingItem(SettingItem {
7299                title: "Remove Trailing Whitespace On Save",
7300                description: "Whether or not to remove any trailing whitespace from lines of a buffer before saving it.",
7301                field: Box::new(SettingField {
7302                    json_path: Some("languages.$(language).remove_trailing_whitespace_on_save"),
7303                    pick: |settings_content| {
7304                        language_settings_field(settings_content, |language| {
7305                            language.remove_trailing_whitespace_on_save.as_ref()
7306                        })
7307                    },
7308                    write: |settings_content, value| {
7309                        language_settings_field_mut(settings_content, value, |language, value| {
7310                            language.remove_trailing_whitespace_on_save = value;
7311                        })
7312                    },
7313                }),
7314                metadata: None,
7315                files: USER | PROJECT,
7316            }),
7317            SettingsPageItem::SettingItem(SettingItem {
7318                title: "Ensure Final Newline On Save",
7319                description: "Whether or not to ensure there's a single newline at the end of a buffer when saving it.",
7320                field: Box::new(SettingField {
7321                    json_path: Some("languages.$(language).ensure_final_newline_on_save"),
7322                    pick: |settings_content| {
7323                        language_settings_field(settings_content, |language| {
7324                            language.ensure_final_newline_on_save.as_ref()
7325                        })
7326                    },
7327                    write: |settings_content, value| {
7328                        language_settings_field_mut(settings_content, value, |language, value| {
7329                            language.ensure_final_newline_on_save = value;
7330                        })
7331                    },
7332                }),
7333                metadata: None,
7334                files: USER | PROJECT,
7335            }),
7336            SettingsPageItem::SettingItem(SettingItem {
7337                title: "Formatter",
7338                description: "How to perform a buffer format.",
7339                field: Box::new(
7340                    SettingField {
7341                        json_path: Some("languages.$(language).formatter"),
7342                        pick: |settings_content| {
7343                            language_settings_field(settings_content, |language| {
7344                                language.formatter.as_ref()
7345                            })
7346                        },
7347                        write: |settings_content, value| {
7348                            language_settings_field_mut(
7349                                settings_content,
7350                                value,
7351                                |language, value| {
7352                                    language.formatter = value;
7353                                },
7354                            )
7355                        },
7356                    }
7357                    .unimplemented(),
7358                ),
7359                metadata: None,
7360                files: USER | PROJECT,
7361            }),
7362            SettingsPageItem::SettingItem(SettingItem {
7363                title: "Use On Type Format",
7364                description: "Whether to use additional LSP queries to format (and amend) the code after every \"trigger\" symbol input, defined by LSP server capabilities",
7365                field: Box::new(SettingField {
7366                    json_path: Some("languages.$(language).use_on_type_format"),
7367                    pick: |settings_content| {
7368                        language_settings_field(settings_content, |language| {
7369                            language.use_on_type_format.as_ref()
7370                        })
7371                    },
7372                    write: |settings_content, value| {
7373                        language_settings_field_mut(settings_content, value, |language, value| {
7374                            language.use_on_type_format = value;
7375                        })
7376                    },
7377                }),
7378                metadata: None,
7379                files: USER | PROJECT,
7380            }),
7381            SettingsPageItem::SettingItem(SettingItem {
7382                title: "Code Actions On Format",
7383                description: "Additional code actions to run when formatting.",
7384                field: Box::new(
7385                    SettingField {
7386                        json_path: Some("languages.$(language).code_actions_on_format"),
7387                        pick: |settings_content| {
7388                            language_settings_field(settings_content, |language| {
7389                                language.code_actions_on_format.as_ref()
7390                            })
7391                        },
7392                        write: |settings_content, value| {
7393                            language_settings_field_mut(
7394                                settings_content,
7395                                value,
7396                                |language, value| {
7397                                    language.code_actions_on_format = value;
7398                                },
7399                            )
7400                        },
7401                    }
7402                    .unimplemented(),
7403                ),
7404                metadata: None,
7405                files: USER | PROJECT,
7406            }),
7407        ]
7408    }
7409
7410    fn autoclose_section() -> [SettingsPageItem; 5] {
7411        [
7412            SettingsPageItem::SectionHeader("Autoclose"),
7413            SettingsPageItem::SettingItem(SettingItem {
7414                title: "Use Autoclose",
7415                description: "Whether to automatically type closing characters for you. For example, when you type '(', Zed will automatically add a closing ')' at the correct position.",
7416                field: Box::new(SettingField {
7417                    json_path: Some("languages.$(language).use_autoclose"),
7418                    pick: |settings_content| {
7419                        language_settings_field(settings_content, |language| {
7420                            language.use_autoclose.as_ref()
7421                        })
7422                    },
7423                    write: |settings_content, value| {
7424                        language_settings_field_mut(settings_content, value, |language, value| {
7425                            language.use_autoclose = value;
7426                        })
7427                    },
7428                }),
7429                metadata: None,
7430                files: USER | PROJECT,
7431            }),
7432            SettingsPageItem::SettingItem(SettingItem {
7433                title: "Use Auto Surround",
7434                description: "Whether to automatically surround text with characters for you. For example, when you select text and type '(', Zed will automatically surround text with ().",
7435                field: Box::new(SettingField {
7436                    json_path: Some("languages.$(language).use_auto_surround"),
7437                    pick: |settings_content| {
7438                        language_settings_field(settings_content, |language| {
7439                            language.use_auto_surround.as_ref()
7440                        })
7441                    },
7442                    write: |settings_content, value| {
7443                        language_settings_field_mut(settings_content, value, |language, value| {
7444                            language.use_auto_surround = value;
7445                        })
7446                    },
7447                }),
7448                metadata: None,
7449                files: USER | PROJECT,
7450            }),
7451            SettingsPageItem::SettingItem(SettingItem {
7452                title: "Always Treat Brackets As Autoclosed",
7453                description: "Controls whether the closing characters are always skipped over and auto-removed no matter how they were inserted.",
7454                field: Box::new(SettingField {
7455                    json_path: Some("languages.$(language).always_treat_brackets_as_autoclosed"),
7456                    pick: |settings_content| {
7457                        language_settings_field(settings_content, |language| {
7458                            language.always_treat_brackets_as_autoclosed.as_ref()
7459                        })
7460                    },
7461                    write: |settings_content, value| {
7462                        language_settings_field_mut(settings_content, value, |language, value| {
7463                            language.always_treat_brackets_as_autoclosed = value;
7464                        })
7465                    },
7466                }),
7467                metadata: None,
7468                files: USER | PROJECT,
7469            }),
7470            SettingsPageItem::SettingItem(SettingItem {
7471                title: "JSX Tag Auto Close",
7472                description: "Whether to automatically close JSX tags.",
7473                field: Box::new(SettingField {
7474                    json_path: Some("languages.$(language).jsx_tag_auto_close"),
7475                    // TODO(settings_ui): this setting should just be a bool
7476                    pick: |settings_content| {
7477                        language_settings_field(settings_content, |language| {
7478                            language.jsx_tag_auto_close.as_ref()?.enabled.as_ref()
7479                        })
7480                    },
7481                    write: |settings_content, value| {
7482                        language_settings_field_mut(settings_content, value, |language, value| {
7483                            language.jsx_tag_auto_close.get_or_insert_default().enabled = value;
7484                        })
7485                    },
7486                }),
7487                metadata: None,
7488                files: USER | PROJECT,
7489            }),
7490        ]
7491    }
7492
7493    fn whitespace_section() -> [SettingsPageItem; 4] {
7494        [
7495            SettingsPageItem::SectionHeader("Whitespace"),
7496            SettingsPageItem::SettingItem(SettingItem {
7497                title: "Show Whitespaces",
7498                description: "Whether to show tabs and spaces in the editor.",
7499                field: Box::new(SettingField {
7500                    json_path: Some("languages.$(language).show_whitespaces"),
7501                    pick: |settings_content| {
7502                        language_settings_field(settings_content, |language| {
7503                            language.show_whitespaces.as_ref()
7504                        })
7505                    },
7506                    write: |settings_content, value| {
7507                        language_settings_field_mut(settings_content, value, |language, value| {
7508                            language.show_whitespaces = value;
7509                        })
7510                    },
7511                }),
7512                metadata: None,
7513                files: USER | PROJECT,
7514            }),
7515            SettingsPageItem::SettingItem(SettingItem {
7516                title: "Space Whitespace Indicator",
7517                description: "Visible character used to render space characters when show_whitespaces is enabled (default: \"\")",
7518                field: Box::new(
7519                    SettingField {
7520                        json_path: Some("languages.$(language).whitespace_map.space"),
7521                        pick: |settings_content| {
7522                            language_settings_field(settings_content, |language| {
7523                                language.whitespace_map.as_ref()?.space.as_ref()
7524                            })
7525                        },
7526                        write: |settings_content, value| {
7527                            language_settings_field_mut(
7528                                settings_content,
7529                                value,
7530                                |language, value| {
7531                                    language.whitespace_map.get_or_insert_default().space = value;
7532                                },
7533                            )
7534                        },
7535                    }
7536                    .unimplemented(),
7537                ),
7538                metadata: None,
7539                files: USER | PROJECT,
7540            }),
7541            SettingsPageItem::SettingItem(SettingItem {
7542                title: "Tab Whitespace Indicator",
7543                description: "Visible character used to render tab characters when show_whitespaces is enabled (default: \"\")",
7544                field: Box::new(
7545                    SettingField {
7546                        json_path: Some("languages.$(language).whitespace_map.tab"),
7547                        pick: |settings_content| {
7548                            language_settings_field(settings_content, |language| {
7549                                language.whitespace_map.as_ref()?.tab.as_ref()
7550                            })
7551                        },
7552                        write: |settings_content, value| {
7553                            language_settings_field_mut(
7554                                settings_content,
7555                                value,
7556                                |language, value| {
7557                                    language.whitespace_map.get_or_insert_default().tab = value;
7558                                },
7559                            )
7560                        },
7561                    }
7562                    .unimplemented(),
7563                ),
7564                metadata: None,
7565                files: USER | PROJECT,
7566            }),
7567        ]
7568    }
7569
7570    fn completions_section() -> [SettingsPageItem; 7] {
7571        [
7572            SettingsPageItem::SectionHeader("Completions"),
7573            SettingsPageItem::SettingItem(SettingItem {
7574                title: "Show Completions On Input",
7575                description: "Whether to pop the completions menu while typing in an editor without explicitly requesting it.",
7576                field: Box::new(SettingField {
7577                    json_path: Some("languages.$(language).show_completions_on_input"),
7578                    pick: |settings_content| {
7579                        language_settings_field(settings_content, |language| {
7580                            language.show_completions_on_input.as_ref()
7581                        })
7582                    },
7583                    write: |settings_content, value| {
7584                        language_settings_field_mut(settings_content, value, |language, value| {
7585                            language.show_completions_on_input = value;
7586                        })
7587                    },
7588                }),
7589                metadata: None,
7590                files: USER | PROJECT,
7591            }),
7592            SettingsPageItem::SettingItem(SettingItem {
7593                title: "Show Completion Documentation",
7594                description: "Whether to display inline and alongside documentation for items in the completions menu.",
7595                field: Box::new(SettingField {
7596                    json_path: Some("languages.$(language).show_completion_documentation"),
7597                    pick: |settings_content| {
7598                        language_settings_field(settings_content, |language| {
7599                            language.show_completion_documentation.as_ref()
7600                        })
7601                    },
7602                    write: |settings_content, value| {
7603                        language_settings_field_mut(settings_content, value, |language, value| {
7604                            language.show_completion_documentation = value;
7605                        })
7606                    },
7607                }),
7608                metadata: None,
7609                files: USER | PROJECT,
7610            }),
7611            SettingsPageItem::SettingItem(SettingItem {
7612                title: "Words",
7613                description: "Controls how words are completed.",
7614                field: Box::new(SettingField {
7615                    json_path: Some("languages.$(language).completions.words"),
7616                    pick: |settings_content| {
7617                        language_settings_field(settings_content, |language| {
7618                            language.completions.as_ref()?.words.as_ref()
7619                        })
7620                    },
7621                    write: |settings_content, value| {
7622                        language_settings_field_mut(settings_content, value, |language, value| {
7623                            language.completions.get_or_insert_default().words = value;
7624                        })
7625                    },
7626                }),
7627                metadata: None,
7628                files: USER | PROJECT,
7629            }),
7630            SettingsPageItem::SettingItem(SettingItem {
7631                title: "Words Min Length",
7632                description: "How many characters has to be in the completions query to automatically show the words-based completions.",
7633                field: Box::new(SettingField {
7634                    json_path: Some("languages.$(language).completions.words_min_length"),
7635                    pick: |settings_content| {
7636                        language_settings_field(settings_content, |language| {
7637                            language.completions.as_ref()?.words_min_length.as_ref()
7638                        })
7639                    },
7640                    write: |settings_content, value| {
7641                        language_settings_field_mut(settings_content, value, |language, value| {
7642                            language
7643                                .completions
7644                                .get_or_insert_default()
7645                                .words_min_length = value;
7646                        })
7647                    },
7648                }),
7649                metadata: None,
7650                files: USER | PROJECT,
7651            }),
7652            SettingsPageItem::SettingItem(SettingItem {
7653                title: "Completion Menu Scrollbar",
7654                description: "When to show the scrollbar in the completion menu.",
7655                field: Box::new(SettingField {
7656                    json_path: Some("editor.completion_menu_scrollbar"),
7657                    pick: |settings_content| {
7658                        settings_content.editor.completion_menu_scrollbar.as_ref()
7659                    },
7660                    write: |settings_content, value| {
7661                        settings_content.editor.completion_menu_scrollbar = value;
7662                    },
7663                }),
7664                metadata: None,
7665                files: USER,
7666            }),
7667            SettingsPageItem::SettingItem(SettingItem {
7668                title: "Completion Detail Alignment",
7669                description: "Whether to align detail text in code completions context menus left or right.",
7670                field: Box::new(SettingField {
7671                    json_path: Some("editor.completion_detail_alignment"),
7672                    pick: |settings_content| {
7673                        settings_content.editor.completion_detail_alignment.as_ref()
7674                    },
7675                    write: |settings_content, value| {
7676                        settings_content.editor.completion_detail_alignment = value;
7677                    },
7678                }),
7679                metadata: None,
7680                files: USER,
7681            }),
7682        ]
7683    }
7684
7685    fn inlay_hints_section() -> [SettingsPageItem; 10] {
7686        [
7687            SettingsPageItem::SectionHeader("Inlay Hints"),
7688            SettingsPageItem::SettingItem(SettingItem {
7689                title: "Enabled",
7690                description: "Global switch to toggle hints on and off.",
7691                field: Box::new(SettingField {
7692                    json_path: Some("languages.$(language).inlay_hints.enabled"),
7693                    pick: |settings_content| {
7694                        language_settings_field(settings_content, |language| {
7695                            language.inlay_hints.as_ref()?.enabled.as_ref()
7696                        })
7697                    },
7698                    write: |settings_content, value| {
7699                        language_settings_field_mut(settings_content, value, |language, value| {
7700                            language.inlay_hints.get_or_insert_default().enabled = value;
7701                        })
7702                    },
7703                }),
7704                metadata: None,
7705                files: USER | PROJECT,
7706            }),
7707            SettingsPageItem::SettingItem(SettingItem {
7708                title: "Show Value Hints",
7709                description: "Global switch to toggle inline values on and off when debugging.",
7710                field: Box::new(SettingField {
7711                    json_path: Some("languages.$(language).inlay_hints.show_value_hints"),
7712                    pick: |settings_content| {
7713                        language_settings_field(settings_content, |language| {
7714                            language.inlay_hints.as_ref()?.show_value_hints.as_ref()
7715                        })
7716                    },
7717                    write: |settings_content, value| {
7718                        language_settings_field_mut(settings_content, value, |language, value| {
7719                            language
7720                                .inlay_hints
7721                                .get_or_insert_default()
7722                                .show_value_hints = value;
7723                        })
7724                    },
7725                }),
7726                metadata: None,
7727                files: USER | PROJECT,
7728            }),
7729            SettingsPageItem::SettingItem(SettingItem {
7730                title: "Show Type Hints",
7731                description: "Whether type hints should be shown.",
7732                field: Box::new(SettingField {
7733                    json_path: Some("languages.$(language).inlay_hints.show_type_hints"),
7734                    pick: |settings_content| {
7735                        language_settings_field(settings_content, |language| {
7736                            language.inlay_hints.as_ref()?.show_type_hints.as_ref()
7737                        })
7738                    },
7739                    write: |settings_content, value| {
7740                        language_settings_field_mut(settings_content, value, |language, value| {
7741                            language.inlay_hints.get_or_insert_default().show_type_hints = value;
7742                        })
7743                    },
7744                }),
7745                metadata: None,
7746                files: USER | PROJECT,
7747            }),
7748            SettingsPageItem::SettingItem(SettingItem {
7749                title: "Show Parameter Hints",
7750                description: "Whether parameter hints should be shown.",
7751                field: Box::new(SettingField {
7752                    json_path: Some("languages.$(language).inlay_hints.show_parameter_hints"),
7753                    pick: |settings_content| {
7754                        language_settings_field(settings_content, |language| {
7755                            language.inlay_hints.as_ref()?.show_parameter_hints.as_ref()
7756                        })
7757                    },
7758                    write: |settings_content, value| {
7759                        language_settings_field_mut(settings_content, value, |language, value| {
7760                            language
7761                                .inlay_hints
7762                                .get_or_insert_default()
7763                                .show_parameter_hints = value;
7764                        })
7765                    },
7766                }),
7767                metadata: None,
7768                files: USER | PROJECT,
7769            }),
7770            SettingsPageItem::SettingItem(SettingItem {
7771                title: "Show Other Hints",
7772                description: "Whether other hints should be shown.",
7773                field: Box::new(SettingField {
7774                    json_path: Some("languages.$(language).inlay_hints.show_other_hints"),
7775                    pick: |settings_content| {
7776                        language_settings_field(settings_content, |language| {
7777                            language.inlay_hints.as_ref()?.show_other_hints.as_ref()
7778                        })
7779                    },
7780                    write: |settings_content, value| {
7781                        language_settings_field_mut(settings_content, value, |language, value| {
7782                            language
7783                                .inlay_hints
7784                                .get_or_insert_default()
7785                                .show_other_hints = value;
7786                        })
7787                    },
7788                }),
7789                metadata: None,
7790                files: USER | PROJECT,
7791            }),
7792            SettingsPageItem::SettingItem(SettingItem {
7793                title: "Show Background",
7794                description: "Show a background for inlay hints.",
7795                field: Box::new(SettingField {
7796                    json_path: Some("languages.$(language).inlay_hints.show_background"),
7797                    pick: |settings_content| {
7798                        language_settings_field(settings_content, |language| {
7799                            language.inlay_hints.as_ref()?.show_background.as_ref()
7800                        })
7801                    },
7802                    write: |settings_content, value| {
7803                        language_settings_field_mut(settings_content, value, |language, value| {
7804                            language.inlay_hints.get_or_insert_default().show_background = value;
7805                        })
7806                    },
7807                }),
7808                metadata: None,
7809                files: USER | PROJECT,
7810            }),
7811            SettingsPageItem::SettingItem(SettingItem {
7812                title: "Edit Debounce Ms",
7813                description: "Whether or not to debounce inlay hints updates after buffer edits (set to 0 to disable debouncing).",
7814                field: Box::new(SettingField {
7815                    json_path: Some("languages.$(language).inlay_hints.edit_debounce_ms"),
7816                    pick: |settings_content| {
7817                        language_settings_field(settings_content, |language| {
7818                            language.inlay_hints.as_ref()?.edit_debounce_ms.as_ref()
7819                        })
7820                    },
7821                    write: |settings_content, value| {
7822                        language_settings_field_mut(settings_content, value, |language, value| {
7823                            language
7824                                .inlay_hints
7825                                .get_or_insert_default()
7826                                .edit_debounce_ms = value;
7827                        })
7828                    },
7829                }),
7830                metadata: None,
7831                files: USER | PROJECT,
7832            }),
7833            SettingsPageItem::SettingItem(SettingItem {
7834                title: "Scroll Debounce Ms",
7835                description: "Whether or not to debounce inlay hints updates after buffer scrolls (set to 0 to disable debouncing).",
7836                field: Box::new(SettingField {
7837                    json_path: Some("languages.$(language).inlay_hints.scroll_debounce_ms"),
7838                    pick: |settings_content| {
7839                        language_settings_field(settings_content, |language| {
7840                            language.inlay_hints.as_ref()?.scroll_debounce_ms.as_ref()
7841                        })
7842                    },
7843                    write: |settings_content, value| {
7844                        language_settings_field_mut(settings_content, value, |language, value| {
7845                            language
7846                                .inlay_hints
7847                                .get_or_insert_default()
7848                                .scroll_debounce_ms = value;
7849                        })
7850                    },
7851                }),
7852                metadata: None,
7853                files: USER | PROJECT,
7854            }),
7855            SettingsPageItem::SettingItem(SettingItem {
7856                title: "Toggle On Modifiers Press",
7857                description: "Toggles inlay hints (hides or shows) when the user presses the modifiers specified.",
7858                field: Box::new(
7859                    SettingField {
7860                        json_path: Some(
7861                            "languages.$(language).inlay_hints.toggle_on_modifiers_press",
7862                        ),
7863                        pick: |settings_content| {
7864                            language_settings_field(settings_content, |language| {
7865                                language
7866                                    .inlay_hints
7867                                    .as_ref()?
7868                                    .toggle_on_modifiers_press
7869                                    .as_ref()
7870                            })
7871                        },
7872                        write: |settings_content, value| {
7873                            language_settings_field_mut(
7874                                settings_content,
7875                                value,
7876                                |language, value| {
7877                                    language
7878                                        .inlay_hints
7879                                        .get_or_insert_default()
7880                                        .toggle_on_modifiers_press = value;
7881                                },
7882                            )
7883                        },
7884                    }
7885                    .unimplemented(),
7886                ),
7887                metadata: None,
7888                files: USER | PROJECT,
7889            }),
7890        ]
7891    }
7892
7893    fn tasks_section() -> [SettingsPageItem; 4] {
7894        [
7895            SettingsPageItem::SectionHeader("Tasks"),
7896            SettingsPageItem::SettingItem(SettingItem {
7897                title: "Enabled",
7898                description: "Whether tasks are enabled for this language.",
7899                field: Box::new(SettingField {
7900                    json_path: Some("languages.$(language).tasks.enabled"),
7901                    pick: |settings_content| {
7902                        language_settings_field(settings_content, |language| {
7903                            language.tasks.as_ref()?.enabled.as_ref()
7904                        })
7905                    },
7906                    write: |settings_content, value| {
7907                        language_settings_field_mut(settings_content, value, |language, value| {
7908                            language.tasks.get_or_insert_default().enabled = value;
7909                        })
7910                    },
7911                }),
7912                metadata: None,
7913                files: USER | PROJECT,
7914            }),
7915            SettingsPageItem::SettingItem(SettingItem {
7916                title: "Variables",
7917                description: "Extra task variables to set for a particular language.",
7918                field: Box::new(
7919                    SettingField {
7920                        json_path: Some("languages.$(language).tasks.variables"),
7921                        pick: |settings_content| {
7922                            language_settings_field(settings_content, |language| {
7923                                language.tasks.as_ref()?.variables.as_ref()
7924                            })
7925                        },
7926                        write: |settings_content, value| {
7927                            language_settings_field_mut(
7928                                settings_content,
7929                                value,
7930                                |language, value| {
7931                                    language.tasks.get_or_insert_default().variables = value;
7932                                },
7933                            )
7934                        },
7935                    }
7936                    .unimplemented(),
7937                ),
7938                metadata: None,
7939                files: USER | PROJECT,
7940            }),
7941            SettingsPageItem::SettingItem(SettingItem {
7942                title: "Prefer LSP",
7943                description: "Use LSP tasks over Zed language extension tasks.",
7944                field: Box::new(SettingField {
7945                    json_path: Some("languages.$(language).tasks.prefer_lsp"),
7946                    pick: |settings_content| {
7947                        language_settings_field(settings_content, |language| {
7948                            language.tasks.as_ref()?.prefer_lsp.as_ref()
7949                        })
7950                    },
7951                    write: |settings_content, value| {
7952                        language_settings_field_mut(settings_content, value, |language, value| {
7953                            language.tasks.get_or_insert_default().prefer_lsp = value;
7954                        })
7955                    },
7956                }),
7957                metadata: None,
7958                files: USER | PROJECT,
7959            }),
7960        ]
7961    }
7962
7963    fn miscellaneous_section() -> [SettingsPageItem; 6] {
7964        [
7965            SettingsPageItem::SectionHeader("Miscellaneous"),
7966            SettingsPageItem::SettingItem(SettingItem {
7967                title: "Word Diff Enabled",
7968                description: "Whether to enable word diff highlighting in the editor. When enabled, changed words within modified lines are highlighted to show exactly what changed.",
7969                field: Box::new(SettingField {
7970                    json_path: Some("languages.$(language).word_diff_enabled"),
7971                    pick: |settings_content| {
7972                        language_settings_field(settings_content, |language| {
7973                            language.word_diff_enabled.as_ref()
7974                        })
7975                    },
7976                    write: |settings_content, value| {
7977                        language_settings_field_mut(settings_content, value, |language, value| {
7978                            language.word_diff_enabled = value;
7979                        })
7980                    },
7981                }),
7982                metadata: None,
7983                files: USER | PROJECT,
7984            }),
7985            SettingsPageItem::SettingItem(SettingItem {
7986                title: "Debuggers",
7987                description: "Preferred debuggers for this language.",
7988                field: Box::new(
7989                    SettingField {
7990                        json_path: Some("languages.$(language).debuggers"),
7991                        pick: |settings_content| {
7992                            language_settings_field(settings_content, |language| {
7993                                language.debuggers.as_ref()
7994                            })
7995                        },
7996                        write: |settings_content, value| {
7997                            language_settings_field_mut(
7998                                settings_content,
7999                                value,
8000                                |language, value| {
8001                                    language.debuggers = value;
8002                                },
8003                            )
8004                        },
8005                    }
8006                    .unimplemented(),
8007                ),
8008                metadata: None,
8009                files: USER | PROJECT,
8010            }),
8011            SettingsPageItem::SettingItem(SettingItem {
8012                title: "Middle Click Paste",
8013                description: "Enable middle-click paste on Linux.",
8014                field: Box::new(SettingField {
8015                    json_path: Some("languages.$(language).editor.middle_click_paste"),
8016                    pick: |settings_content| settings_content.editor.middle_click_paste.as_ref(),
8017                    write: |settings_content, value| {
8018                        settings_content.editor.middle_click_paste = value;
8019                    },
8020                }),
8021                metadata: None,
8022                files: USER,
8023            }),
8024            SettingsPageItem::SettingItem(SettingItem {
8025                title: "Extend Comment On Newline",
8026                description: "Whether to start a new line with a comment when a previous line is a comment as well.",
8027                field: Box::new(SettingField {
8028                    json_path: Some("languages.$(language).extend_comment_on_newline"),
8029                    pick: |settings_content| {
8030                        language_settings_field(settings_content, |language| {
8031                            language.extend_comment_on_newline.as_ref()
8032                        })
8033                    },
8034                    write: |settings_content, value| {
8035                        language_settings_field_mut(settings_content, value, |language, value| {
8036                            language.extend_comment_on_newline = value;
8037                        })
8038                    },
8039                }),
8040                metadata: None,
8041                files: USER | PROJECT,
8042            }),
8043            SettingsPageItem::SettingItem(SettingItem {
8044                title: "Colorize Brackets",
8045                description: "Whether to colorize brackets in the editor.",
8046                field: Box::new(SettingField {
8047                    json_path: Some("languages.$(language).colorize_brackets"),
8048                    pick: |settings_content| {
8049                        language_settings_field(settings_content, |language| {
8050                            language.colorize_brackets.as_ref()
8051                        })
8052                    },
8053                    write: |settings_content, value| {
8054                        language_settings_field_mut(settings_content, value, |language, value| {
8055                            language.colorize_brackets = value;
8056                        })
8057                    },
8058                }),
8059                metadata: None,
8060                files: USER | PROJECT,
8061            }),
8062        ]
8063    }
8064
8065    fn global_only_miscellaneous_sub_section() -> [SettingsPageItem; 3] {
8066        [
8067            SettingsPageItem::SettingItem(SettingItem {
8068                title: "Image Viewer",
8069                description: "The unit for image file sizes.",
8070                field: Box::new(SettingField {
8071                    json_path: Some("image_viewer.unit"),
8072                    pick: |settings_content| {
8073                        settings_content
8074                            .image_viewer
8075                            .as_ref()
8076                            .and_then(|image_viewer| image_viewer.unit.as_ref())
8077                    },
8078                    write: |settings_content, value| {
8079                        settings_content.image_viewer.get_or_insert_default().unit = value;
8080                    },
8081                }),
8082                metadata: None,
8083                files: USER,
8084            }),
8085            SettingsPageItem::SettingItem(SettingItem {
8086                title: "Auto Replace Emoji Shortcode",
8087                description: "Whether to automatically replace emoji shortcodes with emoji characters.",
8088                field: Box::new(SettingField {
8089                    json_path: Some("message_editor.auto_replace_emoji_shortcode"),
8090                    pick: |settings_content| {
8091                        settings_content
8092                            .message_editor
8093                            .as_ref()
8094                            .and_then(|message_editor| {
8095                                message_editor.auto_replace_emoji_shortcode.as_ref()
8096                            })
8097                    },
8098                    write: |settings_content, value| {
8099                        settings_content
8100                            .message_editor
8101                            .get_or_insert_default()
8102                            .auto_replace_emoji_shortcode = value;
8103                    },
8104                }),
8105                metadata: None,
8106                files: USER,
8107            }),
8108            SettingsPageItem::SettingItem(SettingItem {
8109                title: "Drop Size Target",
8110                description: "Relative size of the drop target in the editor that will open dropped file as a split pane.",
8111                field: Box::new(SettingField {
8112                    json_path: Some("drop_target_size"),
8113                    pick: |settings_content| settings_content.workspace.drop_target_size.as_ref(),
8114                    write: |settings_content, value| {
8115                        settings_content.workspace.drop_target_size = value;
8116                    },
8117                }),
8118                metadata: None,
8119                files: USER,
8120            }),
8121        ]
8122    }
8123
8124    let is_global = current_language().is_none();
8125
8126    let lsp_document_colors_item = [SettingsPageItem::SettingItem(SettingItem {
8127        title: "LSP Document Colors",
8128        description: "How to render LSP color previews in the editor.",
8129        field: Box::new(SettingField {
8130            json_path: Some("lsp_document_colors"),
8131            pick: |settings_content| settings_content.editor.lsp_document_colors.as_ref(),
8132            write: |settings_content, value| {
8133                settings_content.editor.lsp_document_colors = value;
8134            },
8135        }),
8136        metadata: None,
8137        files: USER,
8138    })];
8139
8140    if is_global {
8141        concat_sections!(
8142            indentation_section(),
8143            wrapping_section(),
8144            indent_guides_section(),
8145            formatting_section(),
8146            autoclose_section(),
8147            whitespace_section(),
8148            completions_section(),
8149            inlay_hints_section(),
8150            lsp_document_colors_item,
8151            tasks_section(),
8152            miscellaneous_section(),
8153            global_only_miscellaneous_sub_section(),
8154        )
8155    } else {
8156        concat_sections!(
8157            indentation_section(),
8158            wrapping_section(),
8159            indent_guides_section(),
8160            formatting_section(),
8161            autoclose_section(),
8162            whitespace_section(),
8163            completions_section(),
8164            inlay_hints_section(),
8165            tasks_section(),
8166            miscellaneous_section(),
8167        )
8168    }
8169}
8170
8171/// LanguageSettings items that should be included in the "Languages & Tools" page
8172/// not the "Editor" page
8173fn non_editor_language_settings_data() -> Box<[SettingsPageItem]> {
8174    fn lsp_section() -> [SettingsPageItem; 5] {
8175        [
8176            SettingsPageItem::SectionHeader("LSP"),
8177            SettingsPageItem::SettingItem(SettingItem {
8178                title: "Enable Language Server",
8179                description: "Whether to use language servers to provide code intelligence.",
8180                field: Box::new(SettingField {
8181                    json_path: Some("languages.$(language).enable_language_server"),
8182                    pick: |settings_content| {
8183                        language_settings_field(settings_content, |language| {
8184                            language.enable_language_server.as_ref()
8185                        })
8186                    },
8187                    write: |settings_content, value| {
8188                        language_settings_field_mut(settings_content, value, |language, value| {
8189                            language.enable_language_server = value;
8190                        })
8191                    },
8192                }),
8193                metadata: None,
8194                files: USER | PROJECT,
8195            }),
8196            SettingsPageItem::SettingItem(SettingItem {
8197                title: "Language Servers",
8198                description: "The list of language servers to use (or disable) for this language.",
8199                field: Box::new(
8200                    SettingField {
8201                        json_path: Some("languages.$(language).language_servers"),
8202                        pick: |settings_content| {
8203                            language_settings_field(settings_content, |language| {
8204                                language.language_servers.as_ref()
8205                            })
8206                        },
8207                        write: |settings_content, value| {
8208                            language_settings_field_mut(
8209                                settings_content,
8210                                value,
8211                                |language, value| {
8212                                    language.language_servers = value;
8213                                },
8214                            )
8215                        },
8216                    }
8217                    .unimplemented(),
8218                ),
8219                metadata: None,
8220                files: USER | PROJECT,
8221            }),
8222            SettingsPageItem::SettingItem(SettingItem {
8223                title: "Linked Edits",
8224                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.",
8225                field: Box::new(SettingField {
8226                    json_path: Some("languages.$(language).linked_edits"),
8227                    pick: |settings_content| {
8228                        language_settings_field(settings_content, |language| {
8229                            language.linked_edits.as_ref()
8230                        })
8231                    },
8232                    write: |settings_content, value| {
8233                        language_settings_field_mut(settings_content, value, |language, value| {
8234                            language.linked_edits = value;
8235                        })
8236                    },
8237                }),
8238                metadata: None,
8239                files: USER | PROJECT,
8240            }),
8241            SettingsPageItem::SettingItem(SettingItem {
8242                title: "Go To Definition Fallback",
8243                description: "Whether to follow-up empty Go to definition responses from the language server.",
8244                field: Box::new(SettingField {
8245                    json_path: Some("go_to_definition_fallback"),
8246                    pick: |settings_content| {
8247                        settings_content.editor.go_to_definition_fallback.as_ref()
8248                    },
8249                    write: |settings_content, value| {
8250                        settings_content.editor.go_to_definition_fallback = value;
8251                    },
8252                }),
8253                metadata: None,
8254                files: USER,
8255            }),
8256        ]
8257    }
8258
8259    fn lsp_completions_section() -> [SettingsPageItem; 4] {
8260        [
8261            SettingsPageItem::SectionHeader("LSP Completions"),
8262            SettingsPageItem::SettingItem(SettingItem {
8263                title: "Enabled",
8264                description: "Whether to fetch LSP completions or not.",
8265                field: Box::new(SettingField {
8266                    json_path: Some("languages.$(language).completions.lsp"),
8267                    pick: |settings_content| {
8268                        language_settings_field(settings_content, |language| {
8269                            language.completions.as_ref()?.lsp.as_ref()
8270                        })
8271                    },
8272                    write: |settings_content, value| {
8273                        language_settings_field_mut(settings_content, value, |language, value| {
8274                            language.completions.get_or_insert_default().lsp = value;
8275                        })
8276                    },
8277                }),
8278                metadata: None,
8279                files: USER | PROJECT,
8280            }),
8281            SettingsPageItem::SettingItem(SettingItem {
8282                title: "Fetch Timeout (milliseconds)",
8283                description: "When fetching LSP completions, determines how long to wait for a response of a particular server (set to 0 to wait indefinitely).",
8284                field: Box::new(SettingField {
8285                    json_path: Some("languages.$(language).completions.lsp_fetch_timeout_ms"),
8286                    pick: |settings_content| {
8287                        language_settings_field(settings_content, |language| {
8288                            language.completions.as_ref()?.lsp_fetch_timeout_ms.as_ref()
8289                        })
8290                    },
8291                    write: |settings_content, value| {
8292                        language_settings_field_mut(settings_content, value, |language, value| {
8293                            language
8294                                .completions
8295                                .get_or_insert_default()
8296                                .lsp_fetch_timeout_ms = value;
8297                        })
8298                    },
8299                }),
8300                metadata: None,
8301                files: USER | PROJECT,
8302            }),
8303            SettingsPageItem::SettingItem(SettingItem {
8304                title: "Insert Mode",
8305                description: "Controls how LSP completions are inserted.",
8306                field: Box::new(SettingField {
8307                    json_path: Some("languages.$(language).completions.lsp_insert_mode"),
8308                    pick: |settings_content| {
8309                        language_settings_field(settings_content, |language| {
8310                            language.completions.as_ref()?.lsp_insert_mode.as_ref()
8311                        })
8312                    },
8313                    write: |settings_content, value| {
8314                        language_settings_field_mut(settings_content, value, |language, value| {
8315                            language.completions.get_or_insert_default().lsp_insert_mode = value;
8316                        })
8317                    },
8318                }),
8319                metadata: None,
8320                files: USER | PROJECT,
8321            }),
8322        ]
8323    }
8324
8325    fn debugger_section() -> [SettingsPageItem; 2] {
8326        [
8327            SettingsPageItem::SectionHeader("Debuggers"),
8328            SettingsPageItem::SettingItem(SettingItem {
8329                title: "Debuggers",
8330                description: "Preferred debuggers for this language.",
8331                field: Box::new(
8332                    SettingField {
8333                        json_path: Some("languages.$(language).debuggers"),
8334                        pick: |settings_content| {
8335                            language_settings_field(settings_content, |language| {
8336                                language.debuggers.as_ref()
8337                            })
8338                        },
8339                        write: |settings_content, value| {
8340                            language_settings_field_mut(
8341                                settings_content,
8342                                value,
8343                                |language, value| {
8344                                    language.debuggers = value;
8345                                },
8346                            )
8347                        },
8348                    }
8349                    .unimplemented(),
8350                ),
8351                metadata: None,
8352                files: USER | PROJECT,
8353            }),
8354        ]
8355    }
8356
8357    fn prettier_section() -> [SettingsPageItem; 5] {
8358        [
8359            SettingsPageItem::SectionHeader("Prettier"),
8360            SettingsPageItem::SettingItem(SettingItem {
8361                title: "Allowed",
8362                description: "Enables or disables formatting with Prettier for a given language.",
8363                field: Box::new(SettingField {
8364                    json_path: Some("languages.$(language).prettier.allowed"),
8365                    pick: |settings_content| {
8366                        language_settings_field(settings_content, |language| {
8367                            language.prettier.as_ref()?.allowed.as_ref()
8368                        })
8369                    },
8370                    write: |settings_content, value| {
8371                        language_settings_field_mut(settings_content, value, |language, value| {
8372                            language.prettier.get_or_insert_default().allowed = value;
8373                        })
8374                    },
8375                }),
8376                metadata: None,
8377                files: USER | PROJECT,
8378            }),
8379            SettingsPageItem::SettingItem(SettingItem {
8380                title: "Parser",
8381                description: "Forces Prettier integration to use a specific parser name when formatting files with the language.",
8382                field: Box::new(SettingField {
8383                    json_path: Some("languages.$(language).prettier.parser"),
8384                    pick: |settings_content| {
8385                        language_settings_field(settings_content, |language| {
8386                            language.prettier.as_ref()?.parser.as_ref()
8387                        })
8388                    },
8389                    write: |settings_content, value| {
8390                        language_settings_field_mut(settings_content, value, |language, value| {
8391                            language.prettier.get_or_insert_default().parser = value;
8392                        })
8393                    },
8394                }),
8395                metadata: None,
8396                files: USER | PROJECT,
8397            }),
8398            SettingsPageItem::SettingItem(SettingItem {
8399                title: "Plugins",
8400                description: "Forces Prettier integration to use specific plugins when formatting files with the language.",
8401                field: Box::new(
8402                    SettingField {
8403                        json_path: Some("languages.$(language).prettier.plugins"),
8404                        pick: |settings_content| {
8405                            language_settings_field(settings_content, |language| {
8406                                language.prettier.as_ref()?.plugins.as_ref()
8407                            })
8408                        },
8409                        write: |settings_content, value| {
8410                            language_settings_field_mut(
8411                                settings_content,
8412                                value,
8413                                |language, value| {
8414                                    language.prettier.get_or_insert_default().plugins = value;
8415                                },
8416                            )
8417                        },
8418                    }
8419                    .unimplemented(),
8420                ),
8421                metadata: None,
8422                files: USER | PROJECT,
8423            }),
8424            SettingsPageItem::SettingItem(SettingItem {
8425                title: "Options",
8426                description: "Default Prettier options, in the format as in package.json section for Prettier.",
8427                field: Box::new(
8428                    SettingField {
8429                        json_path: Some("languages.$(language).prettier.options"),
8430                        pick: |settings_content| {
8431                            language_settings_field(settings_content, |language| {
8432                                language.prettier.as_ref()?.options.as_ref()
8433                            })
8434                        },
8435                        write: |settings_content, value| {
8436                            language_settings_field_mut(
8437                                settings_content,
8438                                value,
8439                                |language, value| {
8440                                    language.prettier.get_or_insert_default().options = value;
8441                                },
8442                            )
8443                        },
8444                    }
8445                    .unimplemented(),
8446                ),
8447                metadata: None,
8448                files: USER | PROJECT,
8449            }),
8450        ]
8451    }
8452
8453    concat_sections!(
8454        lsp_section(),
8455        lsp_completions_section(),
8456        debugger_section(),
8457        prettier_section(),
8458    )
8459}
8460
8461fn edit_prediction_language_settings_section() -> [SettingsPageItem; 4] {
8462    [
8463        SettingsPageItem::SectionHeader("Edit Predictions"),
8464        SettingsPageItem::SubPageLink(SubPageLink {
8465            title: "Configure Providers".into(),
8466            json_path: Some("edit_predictions.providers"),
8467            description: Some("Set up different edit prediction providers in complement to Zed's built-in Zeta model.".into()),
8468            in_json: false,
8469            files: USER,
8470            render: Arc::new(|_, window, cx| {
8471                let settings_window = cx.entity();
8472                let page = window.use_state(cx, |_, _| {
8473                    crate::pages::EditPredictionSetupPage::new(settings_window)
8474                });
8475                page.into_any_element()
8476            }),
8477        }),
8478        SettingsPageItem::SettingItem(SettingItem {
8479            title: "Show Edit Predictions",
8480            description: "Controls whether edit predictions are shown immediately or manually.",
8481            field: Box::new(SettingField {
8482                json_path: Some("languages.$(language).show_edit_predictions"),
8483                pick: |settings_content| {
8484                    language_settings_field(settings_content, |language| {
8485                        language.show_edit_predictions.as_ref()
8486                    })
8487                },
8488                write: |settings_content, value| {
8489                    language_settings_field_mut(settings_content, value, |language, value| {
8490                        language.show_edit_predictions = value;
8491                    })
8492                },
8493            }),
8494            metadata: None,
8495            files: USER | PROJECT,
8496        }),
8497        SettingsPageItem::SettingItem(SettingItem {
8498            title: "Disable in Language Scopes",
8499            description: "Controls whether edit predictions are shown in the given language scopes.",
8500            field: Box::new(
8501                SettingField {
8502                    json_path: Some("languages.$(language).edit_predictions_disabled_in"),
8503                    pick: |settings_content| {
8504                        language_settings_field(settings_content, |language| {
8505                            language.edit_predictions_disabled_in.as_ref()
8506                        })
8507                    },
8508                    write: |settings_content, value| {
8509                        language_settings_field_mut(settings_content, value, |language, value| {
8510                            language.edit_predictions_disabled_in = value;
8511                        })
8512                    },
8513                }
8514                .unimplemented(),
8515            ),
8516            metadata: None,
8517            files: USER | PROJECT,
8518        }),
8519    ]
8520}
8521
8522fn show_scrollbar_or_editor(
8523    settings_content: &SettingsContent,
8524    show: fn(&SettingsContent) -> Option<&settings::ShowScrollbar>,
8525) -> Option<&settings::ShowScrollbar> {
8526    show(settings_content).or(settings_content
8527        .editor
8528        .scrollbar
8529        .as_ref()
8530        .and_then(|scrollbar| scrollbar.show.as_ref()))
8531}
8532
8533fn dynamic_variants<T>() -> &'static [T::Discriminant]
8534where
8535    T: strum::IntoDiscriminant,
8536    T::Discriminant: strum::VariantArray,
8537{
8538    <<T as strum::IntoDiscriminant>::Discriminant as strum::VariantArray>::VARIANTS
8539}