page_data.rs

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