page_data.rs

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