page_data.rs

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