page_data.rs

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