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; 6] {
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            SettingsPageItem::SettingItem(SettingItem {
3246                title: "Git Status",
3247                description: "Show the Git status in the file finder.",
3248                field: Box::new(SettingField {
3249                    json_path: Some("file_finder.git_status"),
3250                    pick: |settings_content| {
3251                        settings_content.file_finder.as_ref()?.git_status.as_ref()
3252                    },
3253                    write: |settings_content, value| {
3254                        settings_content
3255                            .file_finder
3256                            .get_or_insert_default()
3257                            .git_status = value;
3258                    },
3259                }),
3260                metadata: None,
3261                files: USER,
3262            }),
3263        ]
3264    }
3265
3266    fn file_scan_section() -> [SettingsPageItem; 5] {
3267        [
3268            SettingsPageItem::SectionHeader("File Scan"),
3269            SettingsPageItem::SettingItem(SettingItem {
3270                title: "File Scan Exclusions",
3271                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\"",
3272                field: Box::new(
3273                    SettingField {
3274                        json_path: Some("file_scan_exclusions"),
3275                        pick: |settings_content| {
3276                            settings_content
3277                                .project
3278                                .worktree
3279                                .file_scan_exclusions
3280                                .as_ref()
3281                        },
3282                        write: |settings_content, value| {
3283                            settings_content.project.worktree.file_scan_exclusions = value;
3284                        },
3285                    }
3286                    .unimplemented(),
3287                ),
3288                metadata: None,
3289                files: USER,
3290            }),
3291            SettingsPageItem::SettingItem(SettingItem {
3292                title: "File Scan Inclusions",
3293                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",
3294                field: Box::new(
3295                    SettingField {
3296                        json_path: Some("file_scan_inclusions"),
3297                        pick: |settings_content| {
3298                            settings_content
3299                                .project
3300                                .worktree
3301                                .file_scan_inclusions
3302                                .as_ref()
3303                        },
3304                        write: |settings_content, value| {
3305                            settings_content.project.worktree.file_scan_inclusions = value;
3306                        },
3307                    }
3308                    .unimplemented(),
3309                ),
3310                metadata: None,
3311                files: USER,
3312            }),
3313            SettingsPageItem::SettingItem(SettingItem {
3314                title: "Restore File State",
3315                description: "Restore previous file state when reopening.",
3316                field: Box::new(SettingField {
3317                    json_path: Some("restore_on_file_reopen"),
3318                    pick: |settings_content| {
3319                        settings_content.workspace.restore_on_file_reopen.as_ref()
3320                    },
3321                    write: |settings_content, value| {
3322                        settings_content.workspace.restore_on_file_reopen = value;
3323                    },
3324                }),
3325                metadata: None,
3326                files: USER,
3327            }),
3328            SettingsPageItem::SettingItem(SettingItem {
3329                title: "Close on File Delete",
3330                description: "Automatically close files that have been deleted.",
3331                field: Box::new(SettingField {
3332                    json_path: Some("close_on_file_delete"),
3333                    pick: |settings_content| {
3334                        settings_content.workspace.close_on_file_delete.as_ref()
3335                    },
3336                    write: |settings_content, value| {
3337                        settings_content.workspace.close_on_file_delete = value;
3338                    },
3339                }),
3340                metadata: None,
3341                files: USER,
3342            }),
3343        ]
3344    }
3345
3346    SettingsPage {
3347        title: "Search & Files",
3348        items: concat_sections![search_section(), file_finder_section(), file_scan_section()],
3349    }
3350}
3351
3352fn window_and_layout_page() -> SettingsPage {
3353    fn status_bar_section() -> [SettingsPageItem; 9] {
3354        [
3355            SettingsPageItem::SectionHeader("Status Bar"),
3356            SettingsPageItem::SettingItem(SettingItem {
3357                title: "Project Panel Button",
3358                description: "Show the project panel button in the status bar.",
3359                field: Box::new(SettingField {
3360                    json_path: Some("project_panel.button"),
3361                    pick: |settings_content| {
3362                        settings_content.project_panel.as_ref()?.button.as_ref()
3363                    },
3364                    write: |settings_content, value| {
3365                        settings_content
3366                            .project_panel
3367                            .get_or_insert_default()
3368                            .button = value;
3369                    },
3370                }),
3371                metadata: None,
3372                files: USER,
3373            }),
3374            SettingsPageItem::SettingItem(SettingItem {
3375                title: "Active Language Button",
3376                description: "Show the active language button in the status bar.",
3377                field: Box::new(SettingField {
3378                    json_path: Some("status_bar.active_language_button"),
3379                    pick: |settings_content| {
3380                        settings_content
3381                            .status_bar
3382                            .as_ref()?
3383                            .active_language_button
3384                            .as_ref()
3385                    },
3386                    write: |settings_content, value| {
3387                        settings_content
3388                            .status_bar
3389                            .get_or_insert_default()
3390                            .active_language_button = value;
3391                    },
3392                }),
3393                metadata: None,
3394                files: USER,
3395            }),
3396            SettingsPageItem::SettingItem(SettingItem {
3397                title: "Active Encoding Button",
3398                description: "Control when to show the active encoding in the status bar.",
3399                field: Box::new(SettingField {
3400                    json_path: Some("status_bar.active_encoding_button"),
3401                    pick: |settings_content| {
3402                        settings_content
3403                            .status_bar
3404                            .as_ref()?
3405                            .active_encoding_button
3406                            .as_ref()
3407                    },
3408                    write: |settings_content, value| {
3409                        settings_content
3410                            .status_bar
3411                            .get_or_insert_default()
3412                            .active_encoding_button = value;
3413                    },
3414                }),
3415                metadata: None,
3416                files: USER,
3417            }),
3418            SettingsPageItem::SettingItem(SettingItem {
3419                title: "Cursor Position Button",
3420                description: "Show the cursor position button in the status bar.",
3421                field: Box::new(SettingField {
3422                    json_path: Some("status_bar.cursor_position_button"),
3423                    pick: |settings_content| {
3424                        settings_content
3425                            .status_bar
3426                            .as_ref()?
3427                            .cursor_position_button
3428                            .as_ref()
3429                    },
3430                    write: |settings_content, value| {
3431                        settings_content
3432                            .status_bar
3433                            .get_or_insert_default()
3434                            .cursor_position_button = value;
3435                    },
3436                }),
3437                metadata: None,
3438                files: USER,
3439            }),
3440            SettingsPageItem::SettingItem(SettingItem {
3441                title: "Terminal Button",
3442                description: "Show the terminal button in the status bar.",
3443                field: Box::new(SettingField {
3444                    json_path: Some("terminal.button"),
3445                    pick: |settings_content| settings_content.terminal.as_ref()?.button.as_ref(),
3446                    write: |settings_content, value| {
3447                        settings_content.terminal.get_or_insert_default().button = value;
3448                    },
3449                }),
3450                metadata: None,
3451                files: USER,
3452            }),
3453            SettingsPageItem::SettingItem(SettingItem {
3454                title: "Diagnostics Button",
3455                description: "Show the project diagnostics button in the status bar.",
3456                field: Box::new(SettingField {
3457                    json_path: Some("diagnostics.button"),
3458                    pick: |settings_content| settings_content.diagnostics.as_ref()?.button.as_ref(),
3459                    write: |settings_content, value| {
3460                        settings_content.diagnostics.get_or_insert_default().button = value;
3461                    },
3462                }),
3463                metadata: None,
3464                files: USER,
3465            }),
3466            SettingsPageItem::SettingItem(SettingItem {
3467                title: "Project Search Button",
3468                description: "Show the project search button in the status bar.",
3469                field: Box::new(SettingField {
3470                    json_path: Some("search.button"),
3471                    pick: |settings_content| {
3472                        settings_content.editor.search.as_ref()?.button.as_ref()
3473                    },
3474                    write: |settings_content, value| {
3475                        settings_content
3476                            .editor
3477                            .search
3478                            .get_or_insert_default()
3479                            .button = value;
3480                    },
3481                }),
3482                metadata: None,
3483                files: USER,
3484            }),
3485            SettingsPageItem::SettingItem(SettingItem {
3486                title: "Debugger Button",
3487                description: "Show the debugger button in the status bar.",
3488                field: Box::new(SettingField {
3489                    json_path: Some("debugger.button"),
3490                    pick: |settings_content| settings_content.debugger.as_ref()?.button.as_ref(),
3491                    write: |settings_content, value| {
3492                        settings_content.debugger.get_or_insert_default().button = value;
3493                    },
3494                }),
3495                metadata: None,
3496                files: USER,
3497            }),
3498        ]
3499    }
3500
3501    fn title_bar_section() -> [SettingsPageItem; 9] {
3502        [
3503            SettingsPageItem::SectionHeader("Title Bar"),
3504            SettingsPageItem::SettingItem(SettingItem {
3505                title: "Show Branch Icon",
3506                description: "Show the branch icon beside branch switcher in the titlebar.",
3507                field: Box::new(SettingField {
3508                    json_path: Some("title_bar.show_branch_icon"),
3509                    pick: |settings_content| {
3510                        settings_content
3511                            .title_bar
3512                            .as_ref()?
3513                            .show_branch_icon
3514                            .as_ref()
3515                    },
3516                    write: |settings_content, value| {
3517                        settings_content
3518                            .title_bar
3519                            .get_or_insert_default()
3520                            .show_branch_icon = value;
3521                    },
3522                }),
3523                metadata: None,
3524                files: USER,
3525            }),
3526            SettingsPageItem::SettingItem(SettingItem {
3527                title: "Show Branch Name",
3528                description: "Show the branch name button in the titlebar.",
3529                field: Box::new(SettingField {
3530                    json_path: Some("title_bar.show_branch_name"),
3531                    pick: |settings_content| {
3532                        settings_content
3533                            .title_bar
3534                            .as_ref()?
3535                            .show_branch_name
3536                            .as_ref()
3537                    },
3538                    write: |settings_content, value| {
3539                        settings_content
3540                            .title_bar
3541                            .get_or_insert_default()
3542                            .show_branch_name = value;
3543                    },
3544                }),
3545                metadata: None,
3546                files: USER,
3547            }),
3548            SettingsPageItem::SettingItem(SettingItem {
3549                title: "Show Project Items",
3550                description: "Show the project host and name in the titlebar.",
3551                field: Box::new(SettingField {
3552                    json_path: Some("title_bar.show_project_items"),
3553                    pick: |settings_content| {
3554                        settings_content
3555                            .title_bar
3556                            .as_ref()?
3557                            .show_project_items
3558                            .as_ref()
3559                    },
3560                    write: |settings_content, value| {
3561                        settings_content
3562                            .title_bar
3563                            .get_or_insert_default()
3564                            .show_project_items = value;
3565                    },
3566                }),
3567                metadata: None,
3568                files: USER,
3569            }),
3570            SettingsPageItem::SettingItem(SettingItem {
3571                title: "Show Onboarding Banner",
3572                description: "Show banners announcing new features in the titlebar.",
3573                field: Box::new(SettingField {
3574                    json_path: Some("title_bar.show_onboarding_banner"),
3575                    pick: |settings_content| {
3576                        settings_content
3577                            .title_bar
3578                            .as_ref()?
3579                            .show_onboarding_banner
3580                            .as_ref()
3581                    },
3582                    write: |settings_content, value| {
3583                        settings_content
3584                            .title_bar
3585                            .get_or_insert_default()
3586                            .show_onboarding_banner = value;
3587                    },
3588                }),
3589                metadata: None,
3590                files: USER,
3591            }),
3592            SettingsPageItem::SettingItem(SettingItem {
3593                title: "Show Sign In",
3594                description: "Show the sign in button in the titlebar.",
3595                field: Box::new(SettingField {
3596                    json_path: Some("title_bar.show_sign_in"),
3597                    pick: |settings_content| {
3598                        settings_content.title_bar.as_ref()?.show_sign_in.as_ref()
3599                    },
3600                    write: |settings_content, value| {
3601                        settings_content
3602                            .title_bar
3603                            .get_or_insert_default()
3604                            .show_sign_in = value;
3605                    },
3606                }),
3607                metadata: None,
3608                files: USER,
3609            }),
3610            SettingsPageItem::SettingItem(SettingItem {
3611                title: "Show User Menu",
3612                description: "Show the user menu button in the titlebar.",
3613                field: Box::new(SettingField {
3614                    json_path: Some("title_bar.show_user_menu"),
3615                    pick: |settings_content| {
3616                        settings_content.title_bar.as_ref()?.show_user_menu.as_ref()
3617                    },
3618                    write: |settings_content, value| {
3619                        settings_content
3620                            .title_bar
3621                            .get_or_insert_default()
3622                            .show_user_menu = value;
3623                    },
3624                }),
3625                metadata: None,
3626                files: USER,
3627            }),
3628            SettingsPageItem::SettingItem(SettingItem {
3629                title: "Show User Picture",
3630                description: "Show user picture in the titlebar.",
3631                field: Box::new(SettingField {
3632                    json_path: Some("title_bar.show_user_picture"),
3633                    pick: |settings_content| {
3634                        settings_content
3635                            .title_bar
3636                            .as_ref()?
3637                            .show_user_picture
3638                            .as_ref()
3639                    },
3640                    write: |settings_content, value| {
3641                        settings_content
3642                            .title_bar
3643                            .get_or_insert_default()
3644                            .show_user_picture = value;
3645                    },
3646                }),
3647                metadata: None,
3648                files: USER,
3649            }),
3650            SettingsPageItem::SettingItem(SettingItem {
3651                title: "Show Menus",
3652                description: "Show the menus in the titlebar.",
3653                field: Box::new(SettingField {
3654                    json_path: Some("title_bar.show_menus"),
3655                    pick: |settings_content| {
3656                        settings_content.title_bar.as_ref()?.show_menus.as_ref()
3657                    },
3658                    write: |settings_content, value| {
3659                        settings_content
3660                            .title_bar
3661                            .get_or_insert_default()
3662                            .show_menus = value;
3663                    },
3664                }),
3665                metadata: None,
3666                files: USER,
3667            }),
3668        ]
3669    }
3670
3671    fn tab_bar_section() -> [SettingsPageItem; 9] {
3672        [
3673            SettingsPageItem::SectionHeader("Tab Bar"),
3674            SettingsPageItem::SettingItem(SettingItem {
3675                title: "Show Tab Bar",
3676                description: "Show the tab bar in the editor.",
3677                field: Box::new(SettingField {
3678                    json_path: Some("tab_bar.show"),
3679                    pick: |settings_content| settings_content.tab_bar.as_ref()?.show.as_ref(),
3680                    write: |settings_content, value| {
3681                        settings_content.tab_bar.get_or_insert_default().show = value;
3682                    },
3683                }),
3684                metadata: None,
3685                files: USER,
3686            }),
3687            SettingsPageItem::SettingItem(SettingItem {
3688                title: "Show Git Status In Tabs",
3689                description: "Show the Git file status on a tab item.",
3690                field: Box::new(SettingField {
3691                    json_path: Some("tabs.git_status"),
3692                    pick: |settings_content| settings_content.tabs.as_ref()?.git_status.as_ref(),
3693                    write: |settings_content, value| {
3694                        settings_content.tabs.get_or_insert_default().git_status = value;
3695                    },
3696                }),
3697                metadata: None,
3698                files: USER,
3699            }),
3700            SettingsPageItem::SettingItem(SettingItem {
3701                title: "Show File Icons In Tabs",
3702                description: "Show the file icon for a tab.",
3703                field: Box::new(SettingField {
3704                    json_path: Some("tabs.file_icons"),
3705                    pick: |settings_content| settings_content.tabs.as_ref()?.file_icons.as_ref(),
3706                    write: |settings_content, value| {
3707                        settings_content.tabs.get_or_insert_default().file_icons = value;
3708                    },
3709                }),
3710                metadata: None,
3711                files: USER,
3712            }),
3713            SettingsPageItem::SettingItem(SettingItem {
3714                title: "Tab Close Position",
3715                description: "Position of the close button in a tab.",
3716                field: Box::new(SettingField {
3717                    json_path: Some("tabs.close_position"),
3718                    pick: |settings_content| {
3719                        settings_content.tabs.as_ref()?.close_position.as_ref()
3720                    },
3721                    write: |settings_content, value| {
3722                        settings_content.tabs.get_or_insert_default().close_position = value;
3723                    },
3724                }),
3725                metadata: None,
3726                files: USER,
3727            }),
3728            SettingsPageItem::SettingItem(SettingItem {
3729                files: USER,
3730                title: "Maximum Tabs",
3731                description: "Maximum open tabs in a pane. Will not close an unsaved tab.",
3732                // todo(settings_ui): The default for this value is null and it's use in code
3733                // is complex, so I'm going to come back to this later
3734                field: Box::new(
3735                    SettingField {
3736                        json_path: Some("max_tabs"),
3737                        pick: |settings_content| settings_content.workspace.max_tabs.as_ref(),
3738                        write: |settings_content, value| {
3739                            settings_content.workspace.max_tabs = value;
3740                        },
3741                    }
3742                    .unimplemented(),
3743                ),
3744                metadata: None,
3745            }),
3746            SettingsPageItem::SettingItem(SettingItem {
3747                title: "Show Navigation History Buttons",
3748                description: "Show the navigation history buttons in the tab bar.",
3749                field: Box::new(SettingField {
3750                    json_path: Some("tab_bar.show_nav_history_buttons"),
3751                    pick: |settings_content| {
3752                        settings_content
3753                            .tab_bar
3754                            .as_ref()?
3755                            .show_nav_history_buttons
3756                            .as_ref()
3757                    },
3758                    write: |settings_content, value| {
3759                        settings_content
3760                            .tab_bar
3761                            .get_or_insert_default()
3762                            .show_nav_history_buttons = value;
3763                    },
3764                }),
3765                metadata: None,
3766                files: USER,
3767            }),
3768            SettingsPageItem::SettingItem(SettingItem {
3769                title: "Show Tab Bar Buttons",
3770                description: "Show the tab bar buttons (New, Split Pane, Zoom).",
3771                field: Box::new(SettingField {
3772                    json_path: Some("tab_bar.show_tab_bar_buttons"),
3773                    pick: |settings_content| {
3774                        settings_content
3775                            .tab_bar
3776                            .as_ref()?
3777                            .show_tab_bar_buttons
3778                            .as_ref()
3779                    },
3780                    write: |settings_content, value| {
3781                        settings_content
3782                            .tab_bar
3783                            .get_or_insert_default()
3784                            .show_tab_bar_buttons = value;
3785                    },
3786                }),
3787                metadata: None,
3788                files: USER,
3789            }),
3790            SettingsPageItem::SettingItem(SettingItem {
3791                title: "Pinned Tabs Layout",
3792                description: "Show pinned tabs in a separate row above unpinned tabs.",
3793                field: Box::new(SettingField {
3794                    json_path: Some("tab_bar.show_pinned_tabs_in_separate_row"),
3795                    pick: |settings_content| {
3796                        settings_content
3797                            .tab_bar
3798                            .as_ref()?
3799                            .show_pinned_tabs_in_separate_row
3800                            .as_ref()
3801                    },
3802                    write: |settings_content, value| {
3803                        settings_content
3804                            .tab_bar
3805                            .get_or_insert_default()
3806                            .show_pinned_tabs_in_separate_row = value;
3807                    },
3808                }),
3809                metadata: None,
3810                files: USER,
3811            }),
3812        ]
3813    }
3814
3815    fn tab_settings_section() -> [SettingsPageItem; 4] {
3816        [
3817            SettingsPageItem::SectionHeader("Tab Settings"),
3818            SettingsPageItem::SettingItem(SettingItem {
3819                title: "Activate On Close",
3820                description: "What to do after closing the current tab.",
3821                field: Box::new(SettingField {
3822                    json_path: Some("tabs.activate_on_close"),
3823                    pick: |settings_content| {
3824                        settings_content.tabs.as_ref()?.activate_on_close.as_ref()
3825                    },
3826                    write: |settings_content, value| {
3827                        settings_content
3828                            .tabs
3829                            .get_or_insert_default()
3830                            .activate_on_close = value;
3831                    },
3832                }),
3833                metadata: None,
3834                files: USER,
3835            }),
3836            SettingsPageItem::SettingItem(SettingItem {
3837                title: "Tab Show Diagnostics",
3838                description: "Which files containing diagnostic errors/warnings to mark in the tabs.",
3839                field: Box::new(SettingField {
3840                    json_path: Some("tabs.show_diagnostics"),
3841                    pick: |settings_content| {
3842                        settings_content.tabs.as_ref()?.show_diagnostics.as_ref()
3843                    },
3844                    write: |settings_content, value| {
3845                        settings_content
3846                            .tabs
3847                            .get_or_insert_default()
3848                            .show_diagnostics = value;
3849                    },
3850                }),
3851                metadata: None,
3852                files: USER,
3853            }),
3854            SettingsPageItem::SettingItem(SettingItem {
3855                title: "Show Close Button",
3856                description: "Controls the appearance behavior of the tab's close button.",
3857                field: Box::new(SettingField {
3858                    json_path: Some("tabs.show_close_button"),
3859                    pick: |settings_content| {
3860                        settings_content.tabs.as_ref()?.show_close_button.as_ref()
3861                    },
3862                    write: |settings_content, value| {
3863                        settings_content
3864                            .tabs
3865                            .get_or_insert_default()
3866                            .show_close_button = value;
3867                    },
3868                }),
3869                metadata: None,
3870                files: USER,
3871            }),
3872        ]
3873    }
3874
3875    fn preview_tabs_section() -> [SettingsPageItem; 8] {
3876        [
3877            SettingsPageItem::SectionHeader("Preview Tabs"),
3878            SettingsPageItem::SettingItem(SettingItem {
3879                title: "Preview Tabs Enabled",
3880                description: "Show opened editors as preview tabs.",
3881                field: Box::new(SettingField {
3882                    json_path: Some("preview_tabs.enabled"),
3883                    pick: |settings_content| {
3884                        settings_content.preview_tabs.as_ref()?.enabled.as_ref()
3885                    },
3886                    write: |settings_content, value| {
3887                        settings_content
3888                            .preview_tabs
3889                            .get_or_insert_default()
3890                            .enabled = value;
3891                    },
3892                }),
3893                metadata: None,
3894                files: USER,
3895            }),
3896            SettingsPageItem::SettingItem(SettingItem {
3897                title: "Enable Preview From Project Panel",
3898                description: "Whether to open tabs in preview mode when opened from the project panel with a single click.",
3899                field: Box::new(SettingField {
3900                    json_path: Some("preview_tabs.enable_preview_from_project_panel"),
3901                    pick: |settings_content| {
3902                        settings_content
3903                            .preview_tabs
3904                            .as_ref()?
3905                            .enable_preview_from_project_panel
3906                            .as_ref()
3907                    },
3908                    write: |settings_content, value| {
3909                        settings_content
3910                            .preview_tabs
3911                            .get_or_insert_default()
3912                            .enable_preview_from_project_panel = value;
3913                    },
3914                }),
3915                metadata: None,
3916                files: USER,
3917            }),
3918            SettingsPageItem::SettingItem(SettingItem {
3919                title: "Enable Preview From File Finder",
3920                description: "Whether to open tabs in preview mode when selected from the file finder.",
3921                field: Box::new(SettingField {
3922                    json_path: Some("preview_tabs.enable_preview_from_file_finder"),
3923                    pick: |settings_content| {
3924                        settings_content
3925                            .preview_tabs
3926                            .as_ref()?
3927                            .enable_preview_from_file_finder
3928                            .as_ref()
3929                    },
3930                    write: |settings_content, value| {
3931                        settings_content
3932                            .preview_tabs
3933                            .get_or_insert_default()
3934                            .enable_preview_from_file_finder = value;
3935                    },
3936                }),
3937                metadata: None,
3938                files: USER,
3939            }),
3940            SettingsPageItem::SettingItem(SettingItem {
3941                title: "Enable Preview From Multibuffer",
3942                description: "Whether to open tabs in preview mode when opened from a multibuffer.",
3943                field: Box::new(SettingField {
3944                    json_path: Some("preview_tabs.enable_preview_from_multibuffer"),
3945                    pick: |settings_content| {
3946                        settings_content
3947                            .preview_tabs
3948                            .as_ref()?
3949                            .enable_preview_from_multibuffer
3950                            .as_ref()
3951                    },
3952                    write: |settings_content, value| {
3953                        settings_content
3954                            .preview_tabs
3955                            .get_or_insert_default()
3956                            .enable_preview_from_multibuffer = value;
3957                    },
3958                }),
3959                metadata: None,
3960                files: USER,
3961            }),
3962            SettingsPageItem::SettingItem(SettingItem {
3963                title: "Enable Preview Multibuffer From Code Navigation",
3964                description: "Whether to open tabs in preview mode when code navigation is used to open a multibuffer.",
3965                field: Box::new(SettingField {
3966                    json_path: Some("preview_tabs.enable_preview_multibuffer_from_code_navigation"),
3967                    pick: |settings_content| {
3968                        settings_content
3969                            .preview_tabs
3970                            .as_ref()?
3971                            .enable_preview_multibuffer_from_code_navigation
3972                            .as_ref()
3973                    },
3974                    write: |settings_content, value| {
3975                        settings_content
3976                            .preview_tabs
3977                            .get_or_insert_default()
3978                            .enable_preview_multibuffer_from_code_navigation = value;
3979                    },
3980                }),
3981                metadata: None,
3982                files: USER,
3983            }),
3984            SettingsPageItem::SettingItem(SettingItem {
3985                title: "Enable Preview File From Code Navigation",
3986                description: "Whether to open tabs in preview mode when code navigation is used to open a single file.",
3987                field: Box::new(SettingField {
3988                    json_path: Some("preview_tabs.enable_preview_file_from_code_navigation"),
3989                    pick: |settings_content| {
3990                        settings_content
3991                            .preview_tabs
3992                            .as_ref()?
3993                            .enable_preview_file_from_code_navigation
3994                            .as_ref()
3995                    },
3996                    write: |settings_content, value| {
3997                        settings_content
3998                            .preview_tabs
3999                            .get_or_insert_default()
4000                            .enable_preview_file_from_code_navigation = value;
4001                    },
4002                }),
4003                metadata: None,
4004                files: USER,
4005            }),
4006            SettingsPageItem::SettingItem(SettingItem {
4007                title: "Enable Keep Preview On Code Navigation",
4008                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.",
4009                field: Box::new(SettingField {
4010                    json_path: Some("preview_tabs.enable_keep_preview_on_code_navigation"),
4011                    pick: |settings_content| {
4012                        settings_content
4013                            .preview_tabs
4014                            .as_ref()?
4015                            .enable_keep_preview_on_code_navigation
4016                            .as_ref()
4017                    },
4018                    write: |settings_content, value| {
4019                        settings_content
4020                            .preview_tabs
4021                            .get_or_insert_default()
4022                            .enable_keep_preview_on_code_navigation = value;
4023                    },
4024                }),
4025                metadata: None,
4026                files: USER,
4027            }),
4028        ]
4029    }
4030
4031    fn layout_section() -> [SettingsPageItem; 4] {
4032        [
4033            SettingsPageItem::SectionHeader("Layout"),
4034            SettingsPageItem::SettingItem(SettingItem {
4035                title: "Bottom Dock Layout",
4036                description: "Layout mode for the bottom dock.",
4037                field: Box::new(SettingField {
4038                    json_path: Some("bottom_dock_layout"),
4039                    pick: |settings_content| settings_content.workspace.bottom_dock_layout.as_ref(),
4040                    write: |settings_content, value| {
4041                        settings_content.workspace.bottom_dock_layout = value;
4042                    },
4043                }),
4044                metadata: None,
4045                files: USER,
4046            }),
4047            SettingsPageItem::SettingItem(SettingItem {
4048                files: USER,
4049                title: "Centered Layout Left Padding",
4050                description: "Left padding for centered layout.",
4051                field: Box::new(SettingField {
4052                    json_path: Some("centered_layout.left_padding"),
4053                    pick: |settings_content| {
4054                        settings_content
4055                            .workspace
4056                            .centered_layout
4057                            .as_ref()?
4058                            .left_padding
4059                            .as_ref()
4060                    },
4061                    write: |settings_content, value| {
4062                        settings_content
4063                            .workspace
4064                            .centered_layout
4065                            .get_or_insert_default()
4066                            .left_padding = value;
4067                    },
4068                }),
4069                metadata: None,
4070            }),
4071            SettingsPageItem::SettingItem(SettingItem {
4072                files: USER,
4073                title: "Centered Layout Right Padding",
4074                description: "Right padding for centered layout.",
4075                field: Box::new(SettingField {
4076                    json_path: Some("centered_layout.right_padding"),
4077                    pick: |settings_content| {
4078                        settings_content
4079                            .workspace
4080                            .centered_layout
4081                            .as_ref()?
4082                            .right_padding
4083                            .as_ref()
4084                    },
4085                    write: |settings_content, value| {
4086                        settings_content
4087                            .workspace
4088                            .centered_layout
4089                            .get_or_insert_default()
4090                            .right_padding = value;
4091                    },
4092                }),
4093                metadata: None,
4094            }),
4095        ]
4096    }
4097
4098    fn window_section() -> [SettingsPageItem; 3] {
4099        [
4100            SettingsPageItem::SectionHeader("Window"),
4101            // todo(settings_ui): Should we filter by platform.as_ref()?
4102            SettingsPageItem::SettingItem(SettingItem {
4103                title: "Use System Window Tabs",
4104                description: "(macOS only) whether to allow Windows to tab together.",
4105                field: Box::new(SettingField {
4106                    json_path: Some("use_system_window_tabs"),
4107                    pick: |settings_content| {
4108                        settings_content.workspace.use_system_window_tabs.as_ref()
4109                    },
4110                    write: |settings_content, value| {
4111                        settings_content.workspace.use_system_window_tabs = value;
4112                    },
4113                }),
4114                metadata: None,
4115                files: USER,
4116            }),
4117            SettingsPageItem::SettingItem(SettingItem {
4118                title: "Window Decorations",
4119                description: "(Linux only) whether Zed or your compositor should draw window decorations.",
4120                field: Box::new(SettingField {
4121                    json_path: Some("window_decorations"),
4122                    pick: |settings_content| settings_content.workspace.window_decorations.as_ref(),
4123                    write: |settings_content, value| {
4124                        settings_content.workspace.window_decorations = value;
4125                    },
4126                }),
4127                metadata: None,
4128                files: USER,
4129            }),
4130        ]
4131    }
4132
4133    fn pane_modifiers_section() -> [SettingsPageItem; 4] {
4134        [
4135            SettingsPageItem::SectionHeader("Pane Modifiers"),
4136            SettingsPageItem::SettingItem(SettingItem {
4137                title: "Inactive Opacity",
4138                description: "Opacity of inactive panels (0.0 - 1.0).",
4139                field: Box::new(SettingField {
4140                    json_path: Some("active_pane_modifiers.inactive_opacity"),
4141                    pick: |settings_content| {
4142                        settings_content
4143                            .workspace
4144                            .active_pane_modifiers
4145                            .as_ref()?
4146                            .inactive_opacity
4147                            .as_ref()
4148                    },
4149                    write: |settings_content, value| {
4150                        settings_content
4151                            .workspace
4152                            .active_pane_modifiers
4153                            .get_or_insert_default()
4154                            .inactive_opacity = value;
4155                    },
4156                }),
4157                metadata: None,
4158                files: USER,
4159            }),
4160            SettingsPageItem::SettingItem(SettingItem {
4161                title: "Border Size",
4162                description: "Size of the border surrounding the active pane.",
4163                field: Box::new(SettingField {
4164                    json_path: Some("active_pane_modifiers.border_size"),
4165                    pick: |settings_content| {
4166                        settings_content
4167                            .workspace
4168                            .active_pane_modifiers
4169                            .as_ref()?
4170                            .border_size
4171                            .as_ref()
4172                    },
4173                    write: |settings_content, value| {
4174                        settings_content
4175                            .workspace
4176                            .active_pane_modifiers
4177                            .get_or_insert_default()
4178                            .border_size = value;
4179                    },
4180                }),
4181                metadata: None,
4182                files: USER,
4183            }),
4184            SettingsPageItem::SettingItem(SettingItem {
4185                title: "Zoomed Padding",
4186                description: "Show padding for zoomed panes.",
4187                field: Box::new(SettingField {
4188                    json_path: Some("zoomed_padding"),
4189                    pick: |settings_content| settings_content.workspace.zoomed_padding.as_ref(),
4190                    write: |settings_content, value| {
4191                        settings_content.workspace.zoomed_padding = value;
4192                    },
4193                }),
4194                metadata: None,
4195                files: USER,
4196            }),
4197        ]
4198    }
4199
4200    fn pane_split_direction_section() -> [SettingsPageItem; 3] {
4201        [
4202            SettingsPageItem::SectionHeader("Pane Split Direction"),
4203            SettingsPageItem::SettingItem(SettingItem {
4204                title: "Vertical Split Direction",
4205                description: "Direction to split vertically.",
4206                field: Box::new(SettingField {
4207                    json_path: Some("pane_split_direction_vertical"),
4208                    pick: |settings_content| {
4209                        settings_content
4210                            .workspace
4211                            .pane_split_direction_vertical
4212                            .as_ref()
4213                    },
4214                    write: |settings_content, value| {
4215                        settings_content.workspace.pane_split_direction_vertical = value;
4216                    },
4217                }),
4218                metadata: None,
4219                files: USER,
4220            }),
4221            SettingsPageItem::SettingItem(SettingItem {
4222                title: "Horizontal Split Direction",
4223                description: "Direction to split horizontally.",
4224                field: Box::new(SettingField {
4225                    json_path: Some("pane_split_direction_horizontal"),
4226                    pick: |settings_content| {
4227                        settings_content
4228                            .workspace
4229                            .pane_split_direction_horizontal
4230                            .as_ref()
4231                    },
4232                    write: |settings_content, value| {
4233                        settings_content.workspace.pane_split_direction_horizontal = value;
4234                    },
4235                }),
4236                metadata: None,
4237                files: USER,
4238            }),
4239        ]
4240    }
4241
4242    SettingsPage {
4243        title: "Window & Layout",
4244        items: concat_sections![
4245            status_bar_section(),
4246            title_bar_section(),
4247            tab_bar_section(),
4248            tab_settings_section(),
4249            preview_tabs_section(),
4250            layout_section(),
4251            window_section(),
4252            pane_modifiers_section(),
4253            pane_split_direction_section(),
4254        ],
4255    }
4256}
4257
4258fn panels_page() -> SettingsPage {
4259    fn project_panel_section() -> [SettingsPageItem; 21] {
4260        [
4261            SettingsPageItem::SectionHeader("Project Panel"),
4262            SettingsPageItem::SettingItem(SettingItem {
4263                title: "Project Panel Dock",
4264                description: "Where to dock the project panel.",
4265                field: Box::new(SettingField {
4266                    json_path: Some("project_panel.dock"),
4267                    pick: |settings_content| settings_content.project_panel.as_ref()?.dock.as_ref(),
4268                    write: |settings_content, value| {
4269                        settings_content.project_panel.get_or_insert_default().dock = value;
4270                    },
4271                }),
4272                metadata: None,
4273                files: USER,
4274            }),
4275            SettingsPageItem::SettingItem(SettingItem {
4276                title: "Project Panel Default Width",
4277                description: "Default width of the project panel in pixels.",
4278                field: Box::new(SettingField {
4279                    json_path: Some("project_panel.default_width"),
4280                    pick: |settings_content| {
4281                        settings_content
4282                            .project_panel
4283                            .as_ref()?
4284                            .default_width
4285                            .as_ref()
4286                    },
4287                    write: |settings_content, value| {
4288                        settings_content
4289                            .project_panel
4290                            .get_or_insert_default()
4291                            .default_width = value;
4292                    },
4293                }),
4294                metadata: None,
4295                files: USER,
4296            }),
4297            SettingsPageItem::SettingItem(SettingItem {
4298                title: "Hide .gitignore",
4299                description: "Whether to hide the gitignore entries in the project panel.",
4300                field: Box::new(SettingField {
4301                    json_path: Some("project_panel.hide_gitignore"),
4302                    pick: |settings_content| {
4303                        settings_content
4304                            .project_panel
4305                            .as_ref()?
4306                            .hide_gitignore
4307                            .as_ref()
4308                    },
4309                    write: |settings_content, value| {
4310                        settings_content
4311                            .project_panel
4312                            .get_or_insert_default()
4313                            .hide_gitignore = value;
4314                    },
4315                }),
4316                metadata: None,
4317                files: USER,
4318            }),
4319            SettingsPageItem::SettingItem(SettingItem {
4320                title: "Entry Spacing",
4321                description: "Spacing between worktree entries in the project panel.",
4322                field: Box::new(SettingField {
4323                    json_path: Some("project_panel.entry_spacing"),
4324                    pick: |settings_content| {
4325                        settings_content
4326                            .project_panel
4327                            .as_ref()?
4328                            .entry_spacing
4329                            .as_ref()
4330                    },
4331                    write: |settings_content, value| {
4332                        settings_content
4333                            .project_panel
4334                            .get_or_insert_default()
4335                            .entry_spacing = value;
4336                    },
4337                }),
4338                metadata: None,
4339                files: USER,
4340            }),
4341            SettingsPageItem::SettingItem(SettingItem {
4342                title: "File Icons",
4343                description: "Show file icons in the project panel.",
4344                field: Box::new(SettingField {
4345                    json_path: Some("project_panel.file_icons"),
4346                    pick: |settings_content| {
4347                        settings_content.project_panel.as_ref()?.file_icons.as_ref()
4348                    },
4349                    write: |settings_content, value| {
4350                        settings_content
4351                            .project_panel
4352                            .get_or_insert_default()
4353                            .file_icons = value;
4354                    },
4355                }),
4356                metadata: None,
4357                files: USER,
4358            }),
4359            SettingsPageItem::SettingItem(SettingItem {
4360                title: "Folder Icons",
4361                description: "Whether to show folder icons or chevrons for directories in the project panel.",
4362                field: Box::new(SettingField {
4363                    json_path: Some("project_panel.folder_icons"),
4364                    pick: |settings_content| {
4365                        settings_content
4366                            .project_panel
4367                            .as_ref()?
4368                            .folder_icons
4369                            .as_ref()
4370                    },
4371                    write: |settings_content, value| {
4372                        settings_content
4373                            .project_panel
4374                            .get_or_insert_default()
4375                            .folder_icons = value;
4376                    },
4377                }),
4378                metadata: None,
4379                files: USER,
4380            }),
4381            SettingsPageItem::SettingItem(SettingItem {
4382                title: "Git Status",
4383                description: "Show the Git status in the project panel.",
4384                field: Box::new(SettingField {
4385                    json_path: Some("project_panel.git_status"),
4386                    pick: |settings_content| {
4387                        settings_content.project_panel.as_ref()?.git_status.as_ref()
4388                    },
4389                    write: |settings_content, value| {
4390                        settings_content
4391                            .project_panel
4392                            .get_or_insert_default()
4393                            .git_status = value;
4394                    },
4395                }),
4396                metadata: None,
4397                files: USER,
4398            }),
4399            SettingsPageItem::SettingItem(SettingItem {
4400                title: "Indent Size",
4401                description: "Amount of indentation for nested items.",
4402                field: Box::new(SettingField {
4403                    json_path: Some("project_panel.indent_size"),
4404                    pick: |settings_content| {
4405                        settings_content
4406                            .project_panel
4407                            .as_ref()?
4408                            .indent_size
4409                            .as_ref()
4410                    },
4411                    write: |settings_content, value| {
4412                        settings_content
4413                            .project_panel
4414                            .get_or_insert_default()
4415                            .indent_size = value;
4416                    },
4417                }),
4418                metadata: None,
4419                files: USER,
4420            }),
4421            SettingsPageItem::SettingItem(SettingItem {
4422                title: "Auto Reveal Entries",
4423                description: "Whether to reveal entries in the project panel automatically when a corresponding project entry becomes active.",
4424                field: Box::new(SettingField {
4425                    json_path: Some("project_panel.auto_reveal_entries"),
4426                    pick: |settings_content| {
4427                        settings_content
4428                            .project_panel
4429                            .as_ref()?
4430                            .auto_reveal_entries
4431                            .as_ref()
4432                    },
4433                    write: |settings_content, value| {
4434                        settings_content
4435                            .project_panel
4436                            .get_or_insert_default()
4437                            .auto_reveal_entries = value;
4438                    },
4439                }),
4440                metadata: None,
4441                files: USER,
4442            }),
4443            SettingsPageItem::SettingItem(SettingItem {
4444                title: "Starts Open",
4445                description: "Whether the project panel should open on startup.",
4446                field: Box::new(SettingField {
4447                    json_path: Some("project_panel.starts_open"),
4448                    pick: |settings_content| {
4449                        settings_content
4450                            .project_panel
4451                            .as_ref()?
4452                            .starts_open
4453                            .as_ref()
4454                    },
4455                    write: |settings_content, value| {
4456                        settings_content
4457                            .project_panel
4458                            .get_or_insert_default()
4459                            .starts_open = value;
4460                    },
4461                }),
4462                metadata: None,
4463                files: USER,
4464            }),
4465            SettingsPageItem::SettingItem(SettingItem {
4466                title: "Auto Fold Directories",
4467                description: "Whether to fold directories automatically and show compact folders when a directory has only one subdirectory inside.",
4468                field: Box::new(SettingField {
4469                    json_path: Some("project_panel.auto_fold_dirs"),
4470                    pick: |settings_content| {
4471                        settings_content
4472                            .project_panel
4473                            .as_ref()?
4474                            .auto_fold_dirs
4475                            .as_ref()
4476                    },
4477                    write: |settings_content, value| {
4478                        settings_content
4479                            .project_panel
4480                            .get_or_insert_default()
4481                            .auto_fold_dirs = value;
4482                    },
4483                }),
4484                metadata: None,
4485                files: USER,
4486            }),
4487            SettingsPageItem::SettingItem(SettingItem {
4488                title: "Bold Folder Labels",
4489                description: "Whether to show folder names with bold text in the project panel.",
4490                field: Box::new(SettingField {
4491                    json_path: Some("project_panel.bold_folder_labels"),
4492                    pick: |settings_content| {
4493                        settings_content
4494                            .project_panel
4495                            .as_ref()?
4496                            .bold_folder_labels
4497                            .as_ref()
4498                    },
4499                    write: |settings_content, value| {
4500                        settings_content
4501                            .project_panel
4502                            .get_or_insert_default()
4503                            .bold_folder_labels = value;
4504                    },
4505                }),
4506                metadata: None,
4507                files: USER,
4508            }),
4509            SettingsPageItem::SettingItem(SettingItem {
4510                title: "Show Scrollbar",
4511                description: "Show the scrollbar in the project panel.",
4512                field: Box::new(SettingField {
4513                    json_path: Some("project_panel.scrollbar.show"),
4514                    pick: |settings_content| {
4515                        show_scrollbar_or_editor(settings_content, |settings_content| {
4516                            settings_content
4517                                .project_panel
4518                                .as_ref()?
4519                                .scrollbar
4520                                .as_ref()?
4521                                .show
4522                                .as_ref()
4523                        })
4524                    },
4525                    write: |settings_content, value| {
4526                        settings_content
4527                            .project_panel
4528                            .get_or_insert_default()
4529                            .scrollbar
4530                            .get_or_insert_default()
4531                            .show = value;
4532                    },
4533                }),
4534                metadata: None,
4535                files: USER,
4536            }),
4537            SettingsPageItem::SettingItem(SettingItem {
4538                title: "Show Diagnostics",
4539                description: "Which files containing diagnostic errors/warnings to mark in the project panel.",
4540                field: Box::new(SettingField {
4541                    json_path: Some("project_panel.show_diagnostics"),
4542                    pick: |settings_content| {
4543                        settings_content
4544                            .project_panel
4545                            .as_ref()?
4546                            .show_diagnostics
4547                            .as_ref()
4548                    },
4549                    write: |settings_content, value| {
4550                        settings_content
4551                            .project_panel
4552                            .get_or_insert_default()
4553                            .show_diagnostics = value;
4554                    },
4555                }),
4556                metadata: None,
4557                files: USER,
4558            }),
4559            SettingsPageItem::SettingItem(SettingItem {
4560                title: "Sticky Scroll",
4561                description: "Whether to stick parent directories at top of the project panel.",
4562                field: Box::new(SettingField {
4563                    json_path: Some("project_panel.sticky_scroll"),
4564                    pick: |settings_content| {
4565                        settings_content
4566                            .project_panel
4567                            .as_ref()?
4568                            .sticky_scroll
4569                            .as_ref()
4570                    },
4571                    write: |settings_content, value| {
4572                        settings_content
4573                            .project_panel
4574                            .get_or_insert_default()
4575                            .sticky_scroll = value;
4576                    },
4577                }),
4578                metadata: None,
4579                files: USER,
4580            }),
4581            SettingsPageItem::SettingItem(SettingItem {
4582                files: USER,
4583                title: "Show Indent Guides",
4584                description: "Show indent guides in the project panel.",
4585                field: Box::new(SettingField {
4586                    json_path: Some("project_panel.indent_guides.show"),
4587                    pick: |settings_content| {
4588                        settings_content
4589                            .project_panel
4590                            .as_ref()?
4591                            .indent_guides
4592                            .as_ref()?
4593                            .show
4594                            .as_ref()
4595                    },
4596                    write: |settings_content, value| {
4597                        settings_content
4598                            .project_panel
4599                            .get_or_insert_default()
4600                            .indent_guides
4601                            .get_or_insert_default()
4602                            .show = value;
4603                    },
4604                }),
4605                metadata: None,
4606            }),
4607            SettingsPageItem::SettingItem(SettingItem {
4608                title: "Drag and Drop",
4609                description: "Whether to enable drag-and-drop operations in the project panel.",
4610                field: Box::new(SettingField {
4611                    json_path: Some("project_panel.drag_and_drop"),
4612                    pick: |settings_content| {
4613                        settings_content
4614                            .project_panel
4615                            .as_ref()?
4616                            .drag_and_drop
4617                            .as_ref()
4618                    },
4619                    write: |settings_content, value| {
4620                        settings_content
4621                            .project_panel
4622                            .get_or_insert_default()
4623                            .drag_and_drop = value;
4624                    },
4625                }),
4626                metadata: None,
4627                files: USER,
4628            }),
4629            SettingsPageItem::SettingItem(SettingItem {
4630                title: "Hide Root",
4631                description: "Whether to hide the root entry when only one folder is open in the window.",
4632                field: Box::new(SettingField {
4633                    json_path: Some("project_panel.drag_and_drop"),
4634                    pick: |settings_content| {
4635                        settings_content.project_panel.as_ref()?.hide_root.as_ref()
4636                    },
4637                    write: |settings_content, value| {
4638                        settings_content
4639                            .project_panel
4640                            .get_or_insert_default()
4641                            .hide_root = value;
4642                    },
4643                }),
4644                metadata: None,
4645                files: USER,
4646            }),
4647            SettingsPageItem::SettingItem(SettingItem {
4648                title: "Hide Hidden",
4649                description: "Whether to hide the hidden entries in the project panel.",
4650                field: Box::new(SettingField {
4651                    json_path: Some("project_panel.hide_hidden"),
4652                    pick: |settings_content| {
4653                        settings_content
4654                            .project_panel
4655                            .as_ref()?
4656                            .hide_hidden
4657                            .as_ref()
4658                    },
4659                    write: |settings_content, value| {
4660                        settings_content
4661                            .project_panel
4662                            .get_or_insert_default()
4663                            .hide_hidden = value;
4664                    },
4665                }),
4666                metadata: None,
4667                files: USER,
4668            }),
4669            SettingsPageItem::SettingItem(SettingItem {
4670                title: "Hidden Files",
4671                description: "Globs to match files that will be considered \"hidden\" and can be hidden from the project panel.",
4672                field: Box::new(
4673                    SettingField {
4674                        json_path: Some("worktree.hidden_files"),
4675                        pick: |settings_content| {
4676                            settings_content.project.worktree.hidden_files.as_ref()
4677                        },
4678                        write: |settings_content, value| {
4679                            settings_content.project.worktree.hidden_files = value;
4680                        },
4681                    }
4682                    .unimplemented(),
4683                ),
4684                metadata: None,
4685                files: USER,
4686            }),
4687        ]
4688    }
4689
4690    fn auto_open_files_section() -> [SettingsPageItem; 5] {
4691        [
4692            SettingsPageItem::SectionHeader("Auto Open Files"),
4693            SettingsPageItem::SettingItem(SettingItem {
4694                title: "On Create",
4695                description: "Whether to automatically open newly created files in the editor.",
4696                field: Box::new(SettingField {
4697                    json_path: Some("project_panel.auto_open.on_create"),
4698                    pick: |settings_content| {
4699                        settings_content
4700                            .project_panel
4701                            .as_ref()?
4702                            .auto_open
4703                            .as_ref()?
4704                            .on_create
4705                            .as_ref()
4706                    },
4707                    write: |settings_content, value| {
4708                        settings_content
4709                            .project_panel
4710                            .get_or_insert_default()
4711                            .auto_open
4712                            .get_or_insert_default()
4713                            .on_create = value;
4714                    },
4715                }),
4716                metadata: None,
4717                files: USER,
4718            }),
4719            SettingsPageItem::SettingItem(SettingItem {
4720                title: "On Paste",
4721                description: "Whether to automatically open files after pasting or duplicating them.",
4722                field: Box::new(SettingField {
4723                    json_path: Some("project_panel.auto_open.on_paste"),
4724                    pick: |settings_content| {
4725                        settings_content
4726                            .project_panel
4727                            .as_ref()?
4728                            .auto_open
4729                            .as_ref()?
4730                            .on_paste
4731                            .as_ref()
4732                    },
4733                    write: |settings_content, value| {
4734                        settings_content
4735                            .project_panel
4736                            .get_or_insert_default()
4737                            .auto_open
4738                            .get_or_insert_default()
4739                            .on_paste = value;
4740                    },
4741                }),
4742                metadata: None,
4743                files: USER,
4744            }),
4745            SettingsPageItem::SettingItem(SettingItem {
4746                title: "On Drop",
4747                description: "Whether to automatically open files dropped from external sources.",
4748                field: Box::new(SettingField {
4749                    json_path: Some("project_panel.auto_open.on_drop"),
4750                    pick: |settings_content| {
4751                        settings_content
4752                            .project_panel
4753                            .as_ref()?
4754                            .auto_open
4755                            .as_ref()?
4756                            .on_drop
4757                            .as_ref()
4758                    },
4759                    write: |settings_content, value| {
4760                        settings_content
4761                            .project_panel
4762                            .get_or_insert_default()
4763                            .auto_open
4764                            .get_or_insert_default()
4765                            .on_drop = value;
4766                    },
4767                }),
4768                metadata: None,
4769                files: USER,
4770            }),
4771            SettingsPageItem::SettingItem(SettingItem {
4772                title: "Sort Mode",
4773                description: "Sort order for entries in the project panel.",
4774                field: Box::new(SettingField {
4775                    pick: |settings_content| {
4776                        settings_content.project_panel.as_ref()?.sort_mode.as_ref()
4777                    },
4778                    write: |settings_content, value| {
4779                        settings_content
4780                            .project_panel
4781                            .get_or_insert_default()
4782                            .sort_mode = value;
4783                    },
4784                    json_path: Some("project_panel.sort_mode"),
4785                }),
4786                metadata: None,
4787                files: USER,
4788            }),
4789        ]
4790    }
4791
4792    fn terminal_panel_section() -> [SettingsPageItem; 2] {
4793        [
4794            SettingsPageItem::SectionHeader("Terminal Panel"),
4795            SettingsPageItem::SettingItem(SettingItem {
4796                title: "Terminal Dock",
4797                description: "Where to dock the terminal panel.",
4798                field: Box::new(SettingField {
4799                    json_path: Some("terminal.dock"),
4800                    pick: |settings_content| settings_content.terminal.as_ref()?.dock.as_ref(),
4801                    write: |settings_content, value| {
4802                        settings_content.terminal.get_or_insert_default().dock = value;
4803                    },
4804                }),
4805                metadata: None,
4806                files: USER,
4807            }),
4808        ]
4809    }
4810
4811    fn outline_panel_section() -> [SettingsPageItem; 11] {
4812        [
4813            SettingsPageItem::SectionHeader("Outline Panel"),
4814            SettingsPageItem::SettingItem(SettingItem {
4815                title: "Outline Panel Button",
4816                description: "Show the outline panel button in the status bar.",
4817                field: Box::new(SettingField {
4818                    json_path: Some("outline_panel.button"),
4819                    pick: |settings_content| {
4820                        settings_content.outline_panel.as_ref()?.button.as_ref()
4821                    },
4822                    write: |settings_content, value| {
4823                        settings_content
4824                            .outline_panel
4825                            .get_or_insert_default()
4826                            .button = value;
4827                    },
4828                }),
4829                metadata: None,
4830                files: USER,
4831            }),
4832            SettingsPageItem::SettingItem(SettingItem {
4833                title: "Outline Panel Dock",
4834                description: "Where to dock the outline panel.",
4835                field: Box::new(SettingField {
4836                    json_path: Some("outline_panel.dock"),
4837                    pick: |settings_content| settings_content.outline_panel.as_ref()?.dock.as_ref(),
4838                    write: |settings_content, value| {
4839                        settings_content.outline_panel.get_or_insert_default().dock = value;
4840                    },
4841                }),
4842                metadata: None,
4843                files: USER,
4844            }),
4845            SettingsPageItem::SettingItem(SettingItem {
4846                title: "Outline Panel Default Width",
4847                description: "Default width of the outline panel in pixels.",
4848                field: Box::new(SettingField {
4849                    json_path: Some("outline_panel.default_width"),
4850                    pick: |settings_content| {
4851                        settings_content
4852                            .outline_panel
4853                            .as_ref()?
4854                            .default_width
4855                            .as_ref()
4856                    },
4857                    write: |settings_content, value| {
4858                        settings_content
4859                            .outline_panel
4860                            .get_or_insert_default()
4861                            .default_width = value;
4862                    },
4863                }),
4864                metadata: None,
4865                files: USER,
4866            }),
4867            SettingsPageItem::SettingItem(SettingItem {
4868                title: "File Icons",
4869                description: "Show file icons in the outline panel.",
4870                field: Box::new(SettingField {
4871                    json_path: Some("outline_panel.file_icons"),
4872                    pick: |settings_content| {
4873                        settings_content.outline_panel.as_ref()?.file_icons.as_ref()
4874                    },
4875                    write: |settings_content, value| {
4876                        settings_content
4877                            .outline_panel
4878                            .get_or_insert_default()
4879                            .file_icons = value;
4880                    },
4881                }),
4882                metadata: None,
4883                files: USER,
4884            }),
4885            SettingsPageItem::SettingItem(SettingItem {
4886                title: "Folder Icons",
4887                description: "Whether to show folder icons or chevrons for directories in the outline panel.",
4888                field: Box::new(SettingField {
4889                    json_path: Some("outline_panel.folder_icons"),
4890                    pick: |settings_content| {
4891                        settings_content
4892                            .outline_panel
4893                            .as_ref()?
4894                            .folder_icons
4895                            .as_ref()
4896                    },
4897                    write: |settings_content, value| {
4898                        settings_content
4899                            .outline_panel
4900                            .get_or_insert_default()
4901                            .folder_icons = value;
4902                    },
4903                }),
4904                metadata: None,
4905                files: USER,
4906            }),
4907            SettingsPageItem::SettingItem(SettingItem {
4908                title: "Git Status",
4909                description: "Show the Git status in the outline panel.",
4910                field: Box::new(SettingField {
4911                    json_path: Some("outline_panel.git_status"),
4912                    pick: |settings_content| {
4913                        settings_content.outline_panel.as_ref()?.git_status.as_ref()
4914                    },
4915                    write: |settings_content, value| {
4916                        settings_content
4917                            .outline_panel
4918                            .get_or_insert_default()
4919                            .git_status = value;
4920                    },
4921                }),
4922                metadata: None,
4923                files: USER,
4924            }),
4925            SettingsPageItem::SettingItem(SettingItem {
4926                title: "Indent Size",
4927                description: "Amount of indentation for nested items.",
4928                field: Box::new(SettingField {
4929                    json_path: Some("outline_panel.indent_size"),
4930                    pick: |settings_content| {
4931                        settings_content
4932                            .outline_panel
4933                            .as_ref()?
4934                            .indent_size
4935                            .as_ref()
4936                    },
4937                    write: |settings_content, value| {
4938                        settings_content
4939                            .outline_panel
4940                            .get_or_insert_default()
4941                            .indent_size = value;
4942                    },
4943                }),
4944                metadata: None,
4945                files: USER,
4946            }),
4947            SettingsPageItem::SettingItem(SettingItem {
4948                title: "Auto Reveal Entries",
4949                description: "Whether to reveal when a corresponding outline entry becomes active.",
4950                field: Box::new(SettingField {
4951                    json_path: Some("outline_panel.auto_reveal_entries"),
4952                    pick: |settings_content| {
4953                        settings_content
4954                            .outline_panel
4955                            .as_ref()?
4956                            .auto_reveal_entries
4957                            .as_ref()
4958                    },
4959                    write: |settings_content, value| {
4960                        settings_content
4961                            .outline_panel
4962                            .get_or_insert_default()
4963                            .auto_reveal_entries = value;
4964                    },
4965                }),
4966                metadata: None,
4967                files: USER,
4968            }),
4969            SettingsPageItem::SettingItem(SettingItem {
4970                title: "Auto Fold Directories",
4971                description: "Whether to fold directories automatically when a directory contains only one subdirectory.",
4972                field: Box::new(SettingField {
4973                    json_path: Some("outline_panel.auto_fold_dirs"),
4974                    pick: |settings_content| {
4975                        settings_content
4976                            .outline_panel
4977                            .as_ref()?
4978                            .auto_fold_dirs
4979                            .as_ref()
4980                    },
4981                    write: |settings_content, value| {
4982                        settings_content
4983                            .outline_panel
4984                            .get_or_insert_default()
4985                            .auto_fold_dirs = value;
4986                    },
4987                }),
4988                metadata: None,
4989                files: USER,
4990            }),
4991            SettingsPageItem::SettingItem(SettingItem {
4992                files: USER,
4993                title: "Show Indent Guides",
4994                description: "When to show indent guides in the outline panel.",
4995                field: Box::new(SettingField {
4996                    json_path: Some("outline_panel.indent_guides.show"),
4997                    pick: |settings_content| {
4998                        settings_content
4999                            .outline_panel
5000                            .as_ref()?
5001                            .indent_guides
5002                            .as_ref()?
5003                            .show
5004                            .as_ref()
5005                    },
5006                    write: |settings_content, value| {
5007                        settings_content
5008                            .outline_panel
5009                            .get_or_insert_default()
5010                            .indent_guides
5011                            .get_or_insert_default()
5012                            .show = value;
5013                    },
5014                }),
5015                metadata: None,
5016            }),
5017        ]
5018    }
5019
5020    fn git_panel_section() -> [SettingsPageItem; 10] {
5021        [
5022            SettingsPageItem::SectionHeader("Git Panel"),
5023            SettingsPageItem::SettingItem(SettingItem {
5024                title: "Git Panel Button",
5025                description: "Show the Git panel button in the status bar.",
5026                field: Box::new(SettingField {
5027                    json_path: Some("git_panel.button"),
5028                    pick: |settings_content| settings_content.git_panel.as_ref()?.button.as_ref(),
5029                    write: |settings_content, value| {
5030                        settings_content.git_panel.get_or_insert_default().button = value;
5031                    },
5032                }),
5033                metadata: None,
5034                files: USER,
5035            }),
5036            SettingsPageItem::SettingItem(SettingItem {
5037                title: "Git Panel Dock",
5038                description: "Where to dock the Git panel.",
5039                field: Box::new(SettingField {
5040                    json_path: Some("git_panel.dock"),
5041                    pick: |settings_content| settings_content.git_panel.as_ref()?.dock.as_ref(),
5042                    write: |settings_content, value| {
5043                        settings_content.git_panel.get_or_insert_default().dock = value;
5044                    },
5045                }),
5046                metadata: None,
5047                files: USER,
5048            }),
5049            SettingsPageItem::SettingItem(SettingItem {
5050                title: "Git Panel Default Width",
5051                description: "Default width of the Git panel in pixels.",
5052                field: Box::new(SettingField {
5053                    json_path: Some("git_panel.default_width"),
5054                    pick: |settings_content| {
5055                        settings_content.git_panel.as_ref()?.default_width.as_ref()
5056                    },
5057                    write: |settings_content, value| {
5058                        settings_content
5059                            .git_panel
5060                            .get_or_insert_default()
5061                            .default_width = value;
5062                    },
5063                }),
5064                metadata: None,
5065                files: USER,
5066            }),
5067            SettingsPageItem::SettingItem(SettingItem {
5068                title: "Git Panel Status Style",
5069                description: "How entry statuses are displayed.",
5070                field: Box::new(SettingField {
5071                    json_path: Some("git_panel.status_style"),
5072                    pick: |settings_content| {
5073                        settings_content.git_panel.as_ref()?.status_style.as_ref()
5074                    },
5075                    write: |settings_content, value| {
5076                        settings_content
5077                            .git_panel
5078                            .get_or_insert_default()
5079                            .status_style = value;
5080                    },
5081                }),
5082                metadata: None,
5083                files: USER,
5084            }),
5085            SettingsPageItem::SettingItem(SettingItem {
5086                title: "Fallback Branch Name",
5087                description: "Default branch name will be when init.defaultbranch is not set in Git.",
5088                field: Box::new(SettingField {
5089                    json_path: Some("git_panel.fallback_branch_name"),
5090                    pick: |settings_content| {
5091                        settings_content
5092                            .git_panel
5093                            .as_ref()?
5094                            .fallback_branch_name
5095                            .as_ref()
5096                    },
5097                    write: |settings_content, value| {
5098                        settings_content
5099                            .git_panel
5100                            .get_or_insert_default()
5101                            .fallback_branch_name = value;
5102                    },
5103                }),
5104                metadata: None,
5105                files: USER,
5106            }),
5107            SettingsPageItem::SettingItem(SettingItem {
5108                title: "Sort By Path",
5109                description: "Enable to sort entries in the panel by path, disable to sort by status.",
5110                field: Box::new(SettingField {
5111                    json_path: Some("git_panel.sort_by_path"),
5112                    pick: |settings_content| {
5113                        settings_content.git_panel.as_ref()?.sort_by_path.as_ref()
5114                    },
5115                    write: |settings_content, value| {
5116                        settings_content
5117                            .git_panel
5118                            .get_or_insert_default()
5119                            .sort_by_path = value;
5120                    },
5121                }),
5122                metadata: None,
5123                files: USER,
5124            }),
5125            SettingsPageItem::SettingItem(SettingItem {
5126                title: "Collapse Untracked Diff",
5127                description: "Whether to collapse untracked files in the diff panel.",
5128                field: Box::new(SettingField {
5129                    json_path: Some("git_panel.collapse_untracked_diff"),
5130                    pick: |settings_content| {
5131                        settings_content
5132                            .git_panel
5133                            .as_ref()?
5134                            .collapse_untracked_diff
5135                            .as_ref()
5136                    },
5137                    write: |settings_content, value| {
5138                        settings_content
5139                            .git_panel
5140                            .get_or_insert_default()
5141                            .collapse_untracked_diff = value;
5142                    },
5143                }),
5144                metadata: None,
5145                files: USER,
5146            }),
5147            SettingsPageItem::SettingItem(SettingItem {
5148                title: "Tree View",
5149                description: "Enable to show entries in tree view list, disable to show in flat view list.",
5150                field: Box::new(SettingField {
5151                    json_path: Some("git_panel.tree_view"),
5152                    pick: |settings_content| {
5153                        settings_content.git_panel.as_ref()?.tree_view.as_ref()
5154                    },
5155                    write: |settings_content, value| {
5156                        settings_content.git_panel.get_or_insert_default().tree_view = value;
5157                    },
5158                }),
5159                metadata: None,
5160                files: USER,
5161            }),
5162            SettingsPageItem::SettingItem(SettingItem {
5163                title: "Scroll Bar",
5164                description: "How and when the scrollbar should be displayed.",
5165                field: Box::new(SettingField {
5166                    json_path: Some("git_panel.scrollbar.show"),
5167                    pick: |settings_content| {
5168                        show_scrollbar_or_editor(settings_content, |settings_content| {
5169                            settings_content
5170                                .git_panel
5171                                .as_ref()?
5172                                .scrollbar
5173                                .as_ref()?
5174                                .show
5175                                .as_ref()
5176                        })
5177                    },
5178                    write: |settings_content, value| {
5179                        settings_content
5180                            .git_panel
5181                            .get_or_insert_default()
5182                            .scrollbar
5183                            .get_or_insert_default()
5184                            .show = value;
5185                    },
5186                }),
5187                metadata: None,
5188                files: USER,
5189            }),
5190        ]
5191    }
5192
5193    fn debugger_panel_section() -> [SettingsPageItem; 2] {
5194        [
5195            SettingsPageItem::SectionHeader("Debugger Panel"),
5196            SettingsPageItem::SettingItem(SettingItem {
5197                title: "Debugger Panel Dock",
5198                description: "The dock position of the debug panel.",
5199                field: Box::new(SettingField {
5200                    json_path: Some("debugger.dock"),
5201                    pick: |settings_content| settings_content.debugger.as_ref()?.dock.as_ref(),
5202                    write: |settings_content, value| {
5203                        settings_content.debugger.get_or_insert_default().dock = value;
5204                    },
5205                }),
5206                metadata: None,
5207                files: USER,
5208            }),
5209        ]
5210    }
5211
5212    fn notification_panel_section() -> [SettingsPageItem; 4] {
5213        [
5214            SettingsPageItem::SectionHeader("Notification Panel"),
5215            SettingsPageItem::SettingItem(SettingItem {
5216                title: "Notification Panel Button",
5217                description: "Show the notification panel button in the status bar.",
5218                field: Box::new(SettingField {
5219                    json_path: Some("notification_panel.button"),
5220                    pick: |settings_content| {
5221                        settings_content
5222                            .notification_panel
5223                            .as_ref()?
5224                            .button
5225                            .as_ref()
5226                    },
5227                    write: |settings_content, value| {
5228                        settings_content
5229                            .notification_panel
5230                            .get_or_insert_default()
5231                            .button = value;
5232                    },
5233                }),
5234                metadata: None,
5235                files: USER,
5236            }),
5237            SettingsPageItem::SettingItem(SettingItem {
5238                title: "Notification Panel Dock",
5239                description: "Where to dock the notification panel.",
5240                field: Box::new(SettingField {
5241                    json_path: Some("notification_panel.dock"),
5242                    pick: |settings_content| {
5243                        settings_content.notification_panel.as_ref()?.dock.as_ref()
5244                    },
5245                    write: |settings_content, value| {
5246                        settings_content
5247                            .notification_panel
5248                            .get_or_insert_default()
5249                            .dock = value;
5250                    },
5251                }),
5252                metadata: None,
5253                files: USER,
5254            }),
5255            SettingsPageItem::SettingItem(SettingItem {
5256                title: "Notification Panel Default Width",
5257                description: "Default width of the notification panel in pixels.",
5258                field: Box::new(SettingField {
5259                    json_path: Some("notification_panel.default_width"),
5260                    pick: |settings_content| {
5261                        settings_content
5262                            .notification_panel
5263                            .as_ref()?
5264                            .default_width
5265                            .as_ref()
5266                    },
5267                    write: |settings_content, value| {
5268                        settings_content
5269                            .notification_panel
5270                            .get_or_insert_default()
5271                            .default_width = value;
5272                    },
5273                }),
5274                metadata: None,
5275                files: USER,
5276            }),
5277        ]
5278    }
5279
5280    fn collaboration_panel_section() -> [SettingsPageItem; 4] {
5281        [
5282            SettingsPageItem::SectionHeader("Collaboration Panel"),
5283            SettingsPageItem::SettingItem(SettingItem {
5284                title: "Collaboration Panel Button",
5285                description: "Show the collaboration panel button in the status bar.",
5286                field: Box::new(SettingField {
5287                    json_path: Some("collaboration_panel.button"),
5288                    pick: |settings_content| {
5289                        settings_content
5290                            .collaboration_panel
5291                            .as_ref()?
5292                            .button
5293                            .as_ref()
5294                    },
5295                    write: |settings_content, value| {
5296                        settings_content
5297                            .collaboration_panel
5298                            .get_or_insert_default()
5299                            .button = value;
5300                    },
5301                }),
5302                metadata: None,
5303                files: USER,
5304            }),
5305            SettingsPageItem::SettingItem(SettingItem {
5306                title: "Collaboration Panel Dock",
5307                description: "Where to dock the collaboration panel.",
5308                field: Box::new(SettingField {
5309                    json_path: Some("collaboration_panel.dock"),
5310                    pick: |settings_content| {
5311                        settings_content.collaboration_panel.as_ref()?.dock.as_ref()
5312                    },
5313                    write: |settings_content, value| {
5314                        settings_content
5315                            .collaboration_panel
5316                            .get_or_insert_default()
5317                            .dock = value;
5318                    },
5319                }),
5320                metadata: None,
5321                files: USER,
5322            }),
5323            SettingsPageItem::SettingItem(SettingItem {
5324                title: "Collaboration Panel Default Width",
5325                description: "Default width of the collaboration panel in pixels.",
5326                field: Box::new(SettingField {
5327                    json_path: Some("collaboration_panel.dock"),
5328                    pick: |settings_content| {
5329                        settings_content
5330                            .collaboration_panel
5331                            .as_ref()?
5332                            .default_width
5333                            .as_ref()
5334                    },
5335                    write: |settings_content, value| {
5336                        settings_content
5337                            .collaboration_panel
5338                            .get_or_insert_default()
5339                            .default_width = value;
5340                    },
5341                }),
5342                metadata: None,
5343                files: USER,
5344            }),
5345        ]
5346    }
5347
5348    fn agent_panel_section() -> [SettingsPageItem; 5] {
5349        [
5350            SettingsPageItem::SectionHeader("Agent Panel"),
5351            SettingsPageItem::SettingItem(SettingItem {
5352                title: "Agent Panel Button",
5353                description: "Whether to show the agent panel button in the status bar.",
5354                field: Box::new(SettingField {
5355                    json_path: Some("agent.button"),
5356                    pick: |settings_content| settings_content.agent.as_ref()?.button.as_ref(),
5357                    write: |settings_content, value| {
5358                        settings_content.agent.get_or_insert_default().button = value;
5359                    },
5360                }),
5361                metadata: None,
5362                files: USER,
5363            }),
5364            SettingsPageItem::SettingItem(SettingItem {
5365                title: "Agent Panel Dock",
5366                description: "Where to dock the agent panel.",
5367                field: Box::new(SettingField {
5368                    json_path: Some("agent.dock"),
5369                    pick: |settings_content| settings_content.agent.as_ref()?.dock.as_ref(),
5370                    write: |settings_content, value| {
5371                        settings_content.agent.get_or_insert_default().dock = value;
5372                    },
5373                }),
5374                metadata: None,
5375                files: USER,
5376            }),
5377            SettingsPageItem::SettingItem(SettingItem {
5378                title: "Agent Panel Default Width",
5379                description: "Default width when the agent panel is docked to the left or right.",
5380                field: Box::new(SettingField {
5381                    json_path: Some("agent.default_width"),
5382                    pick: |settings_content| {
5383                        settings_content.agent.as_ref()?.default_width.as_ref()
5384                    },
5385                    write: |settings_content, value| {
5386                        settings_content.agent.get_or_insert_default().default_width = value;
5387                    },
5388                }),
5389                metadata: None,
5390                files: USER,
5391            }),
5392            SettingsPageItem::SettingItem(SettingItem {
5393                title: "Agent Panel Default Height",
5394                description: "Default height when the agent panel is docked to the bottom.",
5395                field: Box::new(SettingField {
5396                    json_path: Some("agent.default_height"),
5397                    pick: |settings_content| {
5398                        settings_content.agent.as_ref()?.default_height.as_ref()
5399                    },
5400                    write: |settings_content, value| {
5401                        settings_content
5402                            .agent
5403                            .get_or_insert_default()
5404                            .default_height = value;
5405                    },
5406                }),
5407                metadata: None,
5408                files: USER,
5409            }),
5410        ]
5411    }
5412
5413    SettingsPage {
5414        title: "Panels",
5415        items: concat_sections![
5416            project_panel_section(),
5417            auto_open_files_section(),
5418            terminal_panel_section(),
5419            outline_panel_section(),
5420            git_panel_section(),
5421            debugger_panel_section(),
5422            notification_panel_section(),
5423            collaboration_panel_section(),
5424            agent_panel_section(),
5425        ],
5426    }
5427}
5428
5429fn debugger_page() -> SettingsPage {
5430    fn general_section() -> [SettingsPageItem; 6] {
5431        [
5432            SettingsPageItem::SectionHeader("General"),
5433            SettingsPageItem::SettingItem(SettingItem {
5434                title: "Stepping Granularity",
5435                description: "Determines the stepping granularity for debug operations.",
5436                field: Box::new(SettingField {
5437                    json_path: Some("debugger.stepping_granularity"),
5438                    pick: |settings_content| {
5439                        settings_content
5440                            .debugger
5441                            .as_ref()?
5442                            .stepping_granularity
5443                            .as_ref()
5444                    },
5445                    write: |settings_content, value| {
5446                        settings_content
5447                            .debugger
5448                            .get_or_insert_default()
5449                            .stepping_granularity = value;
5450                    },
5451                }),
5452                metadata: None,
5453                files: USER,
5454            }),
5455            SettingsPageItem::SettingItem(SettingItem {
5456                title: "Save Breakpoints",
5457                description: "Whether breakpoints should be reused across Zed sessions.",
5458                field: Box::new(SettingField {
5459                    json_path: Some("debugger.save_breakpoints"),
5460                    pick: |settings_content| {
5461                        settings_content
5462                            .debugger
5463                            .as_ref()?
5464                            .save_breakpoints
5465                            .as_ref()
5466                    },
5467                    write: |settings_content, value| {
5468                        settings_content
5469                            .debugger
5470                            .get_or_insert_default()
5471                            .save_breakpoints = value;
5472                    },
5473                }),
5474                metadata: None,
5475                files: USER,
5476            }),
5477            SettingsPageItem::SettingItem(SettingItem {
5478                title: "Timeout",
5479                description: "Time in milliseconds until timeout error when connecting to a TCP debug adapter.",
5480                field: Box::new(SettingField {
5481                    json_path: Some("debugger.timeout"),
5482                    pick: |settings_content| settings_content.debugger.as_ref()?.timeout.as_ref(),
5483                    write: |settings_content, value| {
5484                        settings_content.debugger.get_or_insert_default().timeout = value;
5485                    },
5486                }),
5487                metadata: None,
5488                files: USER,
5489            }),
5490            SettingsPageItem::SettingItem(SettingItem {
5491                title: "Log DAP Communications",
5492                description: "Whether to log messages between active debug adapters and Zed.",
5493                field: Box::new(SettingField {
5494                    json_path: Some("debugger.log_dap_communications"),
5495                    pick: |settings_content| {
5496                        settings_content
5497                            .debugger
5498                            .as_ref()?
5499                            .log_dap_communications
5500                            .as_ref()
5501                    },
5502                    write: |settings_content, value| {
5503                        settings_content
5504                            .debugger
5505                            .get_or_insert_default()
5506                            .log_dap_communications = value;
5507                    },
5508                }),
5509                metadata: None,
5510                files: USER,
5511            }),
5512            SettingsPageItem::SettingItem(SettingItem {
5513                title: "Format DAP Log Messages",
5514                description: "Whether to format DAP messages when adding them to debug adapter logger.",
5515                field: Box::new(SettingField {
5516                    json_path: Some("debugger.format_dap_log_messages"),
5517                    pick: |settings_content| {
5518                        settings_content
5519                            .debugger
5520                            .as_ref()?
5521                            .format_dap_log_messages
5522                            .as_ref()
5523                    },
5524                    write: |settings_content, value| {
5525                        settings_content
5526                            .debugger
5527                            .get_or_insert_default()
5528                            .format_dap_log_messages = value;
5529                    },
5530                }),
5531                metadata: None,
5532                files: USER,
5533            }),
5534        ]
5535    }
5536
5537    SettingsPage {
5538        title: "Debugger",
5539        items: concat_sections![general_section()],
5540    }
5541}
5542
5543fn terminal_page() -> SettingsPage {
5544    fn environment_section() -> [SettingsPageItem; 5] {
5545        [
5546                SettingsPageItem::SectionHeader("Environment"),
5547                SettingsPageItem::DynamicItem(DynamicItem {
5548                    discriminant: SettingItem {
5549                        files: USER | PROJECT,
5550                        title: "Shell",
5551                        description: "What shell to use when opening a terminal.",
5552                        field: Box::new(SettingField {
5553                            json_path: Some("terminal.shell$"),
5554                            pick: |settings_content| {
5555                                Some(&dynamic_variants::<settings::Shell>()[
5556                                    settings_content
5557                                        .terminal
5558                                        .as_ref()?
5559                                        .project
5560                                        .shell
5561                                        .as_ref()?
5562                                        .discriminant() as usize
5563                                ])
5564                            },
5565                            write: |settings_content, value| {
5566                                let Some(value) = value else {
5567                                    if let Some(terminal) = settings_content.terminal.as_mut() {
5568                                        terminal.project.shell = None;
5569                                    }
5570                                    return;
5571                                };
5572                                let settings_value = settings_content
5573                                    .terminal
5574                                    .get_or_insert_default()
5575                                    .project
5576                                    .shell
5577                                    .get_or_insert_with(|| settings::Shell::default());
5578                                let default_shell = if cfg!(target_os = "windows") {
5579                                    "powershell.exe"
5580                                } else {
5581                                    "sh"
5582                                };
5583                                *settings_value = match value {
5584                                    settings::ShellDiscriminants::System => settings::Shell::System,
5585                                    settings::ShellDiscriminants::Program => {
5586                                        let program = match settings_value {
5587                                            settings::Shell::Program(program) => program.clone(),
5588                                            settings::Shell::WithArguments { program, .. } => program.clone(),
5589                                            _ => String::from(default_shell),
5590                                        };
5591                                        settings::Shell::Program(program)
5592                                    }
5593                                    settings::ShellDiscriminants::WithArguments => {
5594                                        let (program, args, title_override) = match settings_value {
5595                                            settings::Shell::Program(program) => (program.clone(), vec![], None),
5596                                            settings::Shell::WithArguments {
5597                                                program,
5598                                                args,
5599                                                title_override,
5600                                            } => (program.clone(), args.clone(), title_override.clone()),
5601                                            _ => (String::from(default_shell), vec![], None),
5602                                        };
5603                                        settings::Shell::WithArguments {
5604                                            program,
5605                                            args,
5606                                            title_override,
5607                                        }
5608                                    }
5609                                };
5610                            },
5611                        }),
5612                        metadata: None,
5613                    },
5614                    pick_discriminant: |settings_content| {
5615                        Some(
5616                            settings_content
5617                                .terminal
5618                                .as_ref()?
5619                                .project
5620                                .shell
5621                                .as_ref()?
5622                                .discriminant() as usize,
5623                        )
5624                    },
5625                    fields: dynamic_variants::<settings::Shell>()
5626                        .into_iter()
5627                        .map(|variant| match variant {
5628                            settings::ShellDiscriminants::System => vec![],
5629                            settings::ShellDiscriminants::Program => vec![SettingItem {
5630                                files: USER | PROJECT,
5631                                title: "Program",
5632                                description: "The shell program to use.",
5633                                field: Box::new(SettingField {
5634                                    json_path: Some("terminal.shell"),
5635                                    pick: |settings_content| match settings_content.terminal.as_ref()?.project.shell.as_ref()
5636                                    {
5637                                        Some(settings::Shell::Program(program)) => Some(program),
5638                                        _ => None,
5639                                    },
5640                                    write: |settings_content, value| {
5641                                        let Some(value) = value else {
5642                                            return;
5643                                        };
5644                                        match settings_content
5645                                            .terminal
5646                                            .get_or_insert_default()
5647                                            .project
5648                                            .shell
5649                                            .as_mut()
5650                                        {
5651                                            Some(settings::Shell::Program(program)) => *program = value,
5652                                            _ => return,
5653                                        }
5654                                    },
5655                                }),
5656                                metadata: None,
5657                            }],
5658                            settings::ShellDiscriminants::WithArguments => vec![
5659                                SettingItem {
5660                                    files: USER | PROJECT,
5661                                    title: "Program",
5662                                    description: "The shell program to run.",
5663                                    field: Box::new(SettingField {
5664                                        json_path: Some("terminal.shell.program"),
5665                                        pick: |settings_content| {
5666                                            match settings_content.terminal.as_ref()?.project.shell.as_ref() {
5667                                                Some(settings::Shell::WithArguments { program, .. }) => Some(program),
5668                                                _ => None,
5669                                            }
5670                                        },
5671                                        write: |settings_content, value| {
5672                                            let Some(value) = value else {
5673                                                return;
5674                                            };
5675                                            match settings_content
5676                                                .terminal
5677                                                .get_or_insert_default()
5678                                                .project
5679                                                .shell
5680                                                .as_mut()
5681                                            {
5682                                                Some(settings::Shell::WithArguments { program, .. }) => {
5683                                                    *program = value
5684                                                }
5685                                                _ => return,
5686                                            }
5687                                        },
5688                                    }),
5689                                    metadata: None,
5690                                },
5691                                SettingItem {
5692                                    files: USER | PROJECT,
5693                                    title: "Arguments",
5694                                    description: "The arguments to pass to the shell program.",
5695                                    field: Box::new(
5696                                        SettingField {
5697                                            json_path: Some("terminal.shell.args"),
5698                                            pick: |settings_content| {
5699                                                match settings_content.terminal.as_ref()?.project.shell.as_ref() {
5700                                                    Some(settings::Shell::WithArguments { args, .. }) => Some(args),
5701                                                    _ => None,
5702                                                }
5703                                            },
5704                                            write: |settings_content, value| {
5705                                                let Some(value) = value else {
5706                                                    return;
5707                                                };
5708                                                match settings_content
5709                                                    .terminal
5710                                                    .get_or_insert_default()
5711                                                    .project
5712                                                    .shell
5713                                                    .as_mut()
5714                                                {
5715                                                    Some(settings::Shell::WithArguments { args, .. }) => *args = value,
5716                                                    _ => return,
5717                                                }
5718                                            },
5719                                        }
5720                                        .unimplemented(),
5721                                    ),
5722                                    metadata: None,
5723                                },
5724                                SettingItem {
5725                                    files: USER | PROJECT,
5726                                    title: "Title Override",
5727                                    description: "An optional string to override the title of the terminal tab.",
5728                                    field: Box::new(SettingField {
5729                                        json_path: Some("terminal.shell.title_override"),
5730                                        pick: |settings_content| {
5731                                            match settings_content.terminal.as_ref()?.project.shell.as_ref() {
5732                                                Some(settings::Shell::WithArguments { title_override, .. }) => {
5733                                                    title_override.as_ref().or(DEFAULT_EMPTY_STRING)
5734                                                }
5735                                                _ => None,
5736                                            }
5737                                        },
5738                                        write: |settings_content, value| {
5739                                            match settings_content
5740                                                .terminal
5741                                                .get_or_insert_default()
5742                                                .project
5743                                                .shell
5744                                                .as_mut()
5745                                            {
5746                                                Some(settings::Shell::WithArguments { title_override, .. }) => {
5747                                                    *title_override = value.filter(|s| !s.is_empty())
5748                                                }
5749                                                _ => return,
5750                                            }
5751                                        },
5752                                    }),
5753                                    metadata: None,
5754                                },
5755                            ],
5756                        })
5757                        .collect(),
5758                }),
5759                SettingsPageItem::DynamicItem(DynamicItem {
5760                    discriminant: SettingItem {
5761                        files: USER | PROJECT,
5762                        title: "Working Directory",
5763                        description: "What working directory to use when launching the terminal.",
5764                        field: Box::new(SettingField {
5765                            json_path: Some("terminal.working_directory$"),
5766                            pick: |settings_content| {
5767                                Some(&dynamic_variants::<settings::WorkingDirectory>()[
5768                                    settings_content
5769                                        .terminal
5770                                        .as_ref()?
5771                                        .project
5772                                        .working_directory
5773                                        .as_ref()?
5774                                        .discriminant() as usize
5775                                ])
5776                            },
5777                            write: |settings_content, value| {
5778                                let Some(value) = value else {
5779                                    if let Some(terminal) = settings_content.terminal.as_mut() {
5780                                        terminal.project.working_directory = None;
5781                                    }
5782                                    return;
5783                                };
5784                                let settings_value = settings_content
5785                                    .terminal
5786                                    .get_or_insert_default()
5787                                    .project
5788                                    .working_directory
5789                                    .get_or_insert_with(|| settings::WorkingDirectory::CurrentProjectDirectory);
5790                                *settings_value = match value {
5791                                    settings::WorkingDirectoryDiscriminants::CurrentFileDirectory => {
5792                                        settings::WorkingDirectory::CurrentFileDirectory
5793                                    },
5794                                    settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => {
5795                                        settings::WorkingDirectory::CurrentProjectDirectory
5796                                    }
5797                                    settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => {
5798                                        settings::WorkingDirectory::FirstProjectDirectory
5799                                    }
5800                                    settings::WorkingDirectoryDiscriminants::AlwaysHome => {
5801                                        settings::WorkingDirectory::AlwaysHome
5802                                    }
5803                                    settings::WorkingDirectoryDiscriminants::Always => {
5804                                        let directory = match settings_value {
5805                                            settings::WorkingDirectory::Always { .. } => return,
5806                                            _ => String::new(),
5807                                        };
5808                                        settings::WorkingDirectory::Always { directory }
5809                                    }
5810                                };
5811                            },
5812                        }),
5813                        metadata: None,
5814                    },
5815                    pick_discriminant: |settings_content| {
5816                        Some(
5817                            settings_content
5818                                .terminal
5819                                .as_ref()?
5820                                .project
5821                                .working_directory
5822                                .as_ref()?
5823                                .discriminant() as usize,
5824                        )
5825                    },
5826                    fields: dynamic_variants::<settings::WorkingDirectory>()
5827                        .into_iter()
5828                        .map(|variant| match variant {
5829                            settings::WorkingDirectoryDiscriminants::CurrentFileDirectory => vec![],
5830                            settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => vec![],
5831                            settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => vec![],
5832                            settings::WorkingDirectoryDiscriminants::AlwaysHome => vec![],
5833                            settings::WorkingDirectoryDiscriminants::Always => vec![SettingItem {
5834                                files: USER | PROJECT,
5835                                title: "Directory",
5836                                description: "The directory path to use (will be shell expanded).",
5837                                field: Box::new(SettingField {
5838                                    json_path: Some("terminal.working_directory.always"),
5839                                    pick: |settings_content| {
5840                                        match settings_content.terminal.as_ref()?.project.working_directory.as_ref() {
5841                                            Some(settings::WorkingDirectory::Always { directory }) => Some(directory),
5842                                            _ => None,
5843                                        }
5844                                    },
5845                                    write: |settings_content, value| {
5846                                        let value = value.unwrap_or_default();
5847                                        match settings_content
5848                                            .terminal
5849                                            .get_or_insert_default()
5850                                            .project
5851                                            .working_directory
5852                                            .as_mut()
5853                                        {
5854                                            Some(settings::WorkingDirectory::Always { directory }) => *directory = value,
5855                                            _ => return,
5856                                        }
5857                                    },
5858                                }),
5859                                metadata: None,
5860                            }],
5861                        })
5862                        .collect(),
5863                }),
5864                SettingsPageItem::SettingItem(SettingItem {
5865                    title: "Environment Variables",
5866                    description: "Key-value pairs to add to the terminal's environment.",
5867                    field: Box::new(
5868                        SettingField {
5869                            json_path: Some("terminal.env"),
5870                            pick: |settings_content| settings_content.terminal.as_ref()?.project.env.as_ref(),
5871                            write: |settings_content, value| {
5872                                settings_content.terminal.get_or_insert_default().project.env = value;
5873                            },
5874                        }
5875                        .unimplemented(),
5876                    ),
5877                    metadata: None,
5878                    files: USER | PROJECT,
5879                }),
5880                SettingsPageItem::SettingItem(SettingItem {
5881                    title: "Detect Virtual Environment",
5882                    description: "Activates the Python virtual environment, if one is found, in the terminal's working directory.",
5883                    field: Box::new(
5884                        SettingField {
5885                            json_path: Some("terminal.detect_venv"),
5886                            pick: |settings_content| settings_content.terminal.as_ref()?.project.detect_venv.as_ref(),
5887                            write: |settings_content, value| {
5888                                settings_content
5889                                    .terminal
5890                                    .get_or_insert_default()
5891                                    .project
5892                                    .detect_venv = value;
5893                            },
5894                        }
5895                        .unimplemented(),
5896                    ),
5897                    metadata: None,
5898                    files: USER | PROJECT,
5899                }),
5900            ]
5901    }
5902
5903    fn font_section() -> [SettingsPageItem; 6] {
5904        [
5905            SettingsPageItem::SectionHeader("Font"),
5906            SettingsPageItem::SettingItem(SettingItem {
5907                title: "Font Size",
5908                description: "Font size for terminal text. If not set, defaults to buffer font size.",
5909                field: Box::new(SettingField {
5910                    json_path: Some("terminal.font_size"),
5911                    pick: |settings_content| {
5912                        settings_content
5913                            .terminal
5914                            .as_ref()
5915                            .and_then(|terminal| terminal.font_size.as_ref())
5916                            .or(settings_content.theme.buffer_font_size.as_ref())
5917                    },
5918                    write: |settings_content, value| {
5919                        settings_content.terminal.get_or_insert_default().font_size = value;
5920                    },
5921                }),
5922                metadata: None,
5923                files: USER,
5924            }),
5925            SettingsPageItem::SettingItem(SettingItem {
5926                title: "Font Family",
5927                description: "Font family for terminal text. If not set, defaults to buffer font family.",
5928                field: Box::new(SettingField {
5929                    json_path: Some("terminal.font_family"),
5930                    pick: |settings_content| {
5931                        settings_content
5932                            .terminal
5933                            .as_ref()
5934                            .and_then(|terminal| terminal.font_family.as_ref())
5935                            .or(settings_content.theme.buffer_font_family.as_ref())
5936                    },
5937                    write: |settings_content, value| {
5938                        settings_content
5939                            .terminal
5940                            .get_or_insert_default()
5941                            .font_family = value;
5942                    },
5943                }),
5944                metadata: None,
5945                files: USER,
5946            }),
5947            SettingsPageItem::SettingItem(SettingItem {
5948                title: "Font Fallbacks",
5949                description: "Font fallbacks for terminal text. If not set, defaults to buffer font fallbacks.",
5950                field: Box::new(
5951                    SettingField {
5952                        json_path: Some("terminal.font_fallbacks"),
5953                        pick: |settings_content| {
5954                            settings_content
5955                                .terminal
5956                                .as_ref()
5957                                .and_then(|terminal| terminal.font_fallbacks.as_ref())
5958                                .or(settings_content.theme.buffer_font_fallbacks.as_ref())
5959                        },
5960                        write: |settings_content, value| {
5961                            settings_content
5962                                .terminal
5963                                .get_or_insert_default()
5964                                .font_fallbacks = value;
5965                        },
5966                    }
5967                    .unimplemented(),
5968                ),
5969                metadata: None,
5970                files: USER,
5971            }),
5972            SettingsPageItem::SettingItem(SettingItem {
5973                title: "Font Weight",
5974                description: "Font weight for terminal text in CSS weight units (100-900).",
5975                field: Box::new(SettingField {
5976                    json_path: Some("terminal.font_weight"),
5977                    pick: |settings_content| {
5978                        settings_content.terminal.as_ref()?.font_weight.as_ref()
5979                    },
5980                    write: |settings_content, value| {
5981                        settings_content
5982                            .terminal
5983                            .get_or_insert_default()
5984                            .font_weight = value;
5985                    },
5986                }),
5987                metadata: None,
5988                files: USER,
5989            }),
5990            SettingsPageItem::SettingItem(SettingItem {
5991                title: "Font Features",
5992                description: "Font features for terminal text.",
5993                field: Box::new(
5994                    SettingField {
5995                        json_path: Some("terminal.font_features"),
5996                        pick: |settings_content| {
5997                            settings_content
5998                                .terminal
5999                                .as_ref()
6000                                .and_then(|terminal| terminal.font_features.as_ref())
6001                                .or(settings_content.theme.buffer_font_features.as_ref())
6002                        },
6003                        write: |settings_content, value| {
6004                            settings_content
6005                                .terminal
6006                                .get_or_insert_default()
6007                                .font_features = value;
6008                        },
6009                    }
6010                    .unimplemented(),
6011                ),
6012                metadata: None,
6013                files: USER,
6014            }),
6015        ]
6016    }
6017
6018    fn display_settings_section() -> [SettingsPageItem; 6] {
6019        [
6020            SettingsPageItem::SectionHeader("Display Settings"),
6021            SettingsPageItem::SettingItem(SettingItem {
6022                title: "Line Height",
6023                description: "Line height for terminal text.",
6024                field: Box::new(
6025                    SettingField {
6026                        json_path: Some("terminal.line_height"),
6027                        pick: |settings_content| {
6028                            settings_content.terminal.as_ref()?.line_height.as_ref()
6029                        },
6030                        write: |settings_content, value| {
6031                            settings_content
6032                                .terminal
6033                                .get_or_insert_default()
6034                                .line_height = value;
6035                        },
6036                    }
6037                    .unimplemented(),
6038                ),
6039                metadata: None,
6040                files: USER,
6041            }),
6042            SettingsPageItem::SettingItem(SettingItem {
6043                title: "Cursor Shape",
6044                description: "Default cursor shape for the terminal (bar, block, underline, or hollow).",
6045                field: Box::new(SettingField {
6046                    json_path: Some("terminal.cursor_shape"),
6047                    pick: |settings_content| {
6048                        settings_content.terminal.as_ref()?.cursor_shape.as_ref()
6049                    },
6050                    write: |settings_content, value| {
6051                        settings_content
6052                            .terminal
6053                            .get_or_insert_default()
6054                            .cursor_shape = value;
6055                    },
6056                }),
6057                metadata: None,
6058                files: USER,
6059            }),
6060            SettingsPageItem::SettingItem(SettingItem {
6061                title: "Cursor Blinking",
6062                description: "Sets the cursor blinking behavior in the terminal.",
6063                field: Box::new(SettingField {
6064                    json_path: Some("terminal.blinking"),
6065                    pick: |settings_content| settings_content.terminal.as_ref()?.blinking.as_ref(),
6066                    write: |settings_content, value| {
6067                        settings_content.terminal.get_or_insert_default().blinking = value;
6068                    },
6069                }),
6070                metadata: None,
6071                files: USER,
6072            }),
6073            SettingsPageItem::SettingItem(SettingItem {
6074                title: "Alternate Scroll",
6075                description: "Whether alternate scroll mode is active by default (converts mouse scroll to arrow keys in apps like Vim).",
6076                field: Box::new(SettingField {
6077                    json_path: Some("terminal.alternate_scroll"),
6078                    pick: |settings_content| {
6079                        settings_content
6080                            .terminal
6081                            .as_ref()?
6082                            .alternate_scroll
6083                            .as_ref()
6084                    },
6085                    write: |settings_content, value| {
6086                        settings_content
6087                            .terminal
6088                            .get_or_insert_default()
6089                            .alternate_scroll = value;
6090                    },
6091                }),
6092                metadata: None,
6093                files: USER,
6094            }),
6095            SettingsPageItem::SettingItem(SettingItem {
6096                title: "Minimum Contrast",
6097                description: "The minimum APCA perceptual contrast between foreground and background colors (0-106).",
6098                field: Box::new(SettingField {
6099                    json_path: Some("terminal.minimum_contrast"),
6100                    pick: |settings_content| {
6101                        settings_content
6102                            .terminal
6103                            .as_ref()?
6104                            .minimum_contrast
6105                            .as_ref()
6106                    },
6107                    write: |settings_content, value| {
6108                        settings_content
6109                            .terminal
6110                            .get_or_insert_default()
6111                            .minimum_contrast = value;
6112                    },
6113                }),
6114                metadata: None,
6115                files: USER,
6116            }),
6117        ]
6118    }
6119
6120    fn behavior_settings_section() -> [SettingsPageItem; 4] {
6121        [
6122            SettingsPageItem::SectionHeader("Behavior Settings"),
6123            SettingsPageItem::SettingItem(SettingItem {
6124                title: "Option As Meta",
6125                description: "Whether the option key behaves as the meta key.",
6126                field: Box::new(SettingField {
6127                    json_path: Some("terminal.option_as_meta"),
6128                    pick: |settings_content| {
6129                        settings_content.terminal.as_ref()?.option_as_meta.as_ref()
6130                    },
6131                    write: |settings_content, value| {
6132                        settings_content
6133                            .terminal
6134                            .get_or_insert_default()
6135                            .option_as_meta = value;
6136                    },
6137                }),
6138                metadata: None,
6139                files: USER,
6140            }),
6141            SettingsPageItem::SettingItem(SettingItem {
6142                title: "Copy On Select",
6143                description: "Whether selecting text in the terminal automatically copies to the system clipboard.",
6144                field: Box::new(SettingField {
6145                    json_path: Some("terminal.copy_on_select"),
6146                    pick: |settings_content| {
6147                        settings_content.terminal.as_ref()?.copy_on_select.as_ref()
6148                    },
6149                    write: |settings_content, value| {
6150                        settings_content
6151                            .terminal
6152                            .get_or_insert_default()
6153                            .copy_on_select = value;
6154                    },
6155                }),
6156                metadata: None,
6157                files: USER,
6158            }),
6159            SettingsPageItem::SettingItem(SettingItem {
6160                title: "Keep Selection On Copy",
6161                description: "Whether to keep the text selection after copying it to the clipboard.",
6162                field: Box::new(SettingField {
6163                    json_path: Some("terminal.keep_selection_on_copy"),
6164                    pick: |settings_content| {
6165                        settings_content
6166                            .terminal
6167                            .as_ref()?
6168                            .keep_selection_on_copy
6169                            .as_ref()
6170                    },
6171                    write: |settings_content, value| {
6172                        settings_content
6173                            .terminal
6174                            .get_or_insert_default()
6175                            .keep_selection_on_copy = value;
6176                    },
6177                }),
6178                metadata: None,
6179                files: USER,
6180            }),
6181        ]
6182    }
6183
6184    fn layout_settings_section() -> [SettingsPageItem; 3] {
6185        [
6186            SettingsPageItem::SectionHeader("Layout Settings"),
6187            SettingsPageItem::SettingItem(SettingItem {
6188                title: "Default Width",
6189                description: "Default width when the terminal is docked to the left or right (in pixels).",
6190                field: Box::new(SettingField {
6191                    json_path: Some("terminal.default_width"),
6192                    pick: |settings_content| {
6193                        settings_content.terminal.as_ref()?.default_width.as_ref()
6194                    },
6195                    write: |settings_content, value| {
6196                        settings_content
6197                            .terminal
6198                            .get_or_insert_default()
6199                            .default_width = value;
6200                    },
6201                }),
6202                metadata: None,
6203                files: USER,
6204            }),
6205            SettingsPageItem::SettingItem(SettingItem {
6206                title: "Default Height",
6207                description: "Default height when the terminal is docked to the bottom (in pixels).",
6208                field: Box::new(SettingField {
6209                    json_path: Some("terminal.default_height"),
6210                    pick: |settings_content| {
6211                        settings_content.terminal.as_ref()?.default_height.as_ref()
6212                    },
6213                    write: |settings_content, value| {
6214                        settings_content
6215                            .terminal
6216                            .get_or_insert_default()
6217                            .default_height = value;
6218                    },
6219                }),
6220                metadata: None,
6221                files: USER,
6222            }),
6223        ]
6224    }
6225
6226    fn advanced_settings_section() -> [SettingsPageItem; 3] {
6227        [
6228            SettingsPageItem::SectionHeader("Advanced Settings"),
6229            SettingsPageItem::SettingItem(SettingItem {
6230                title: "Max Scroll History Lines",
6231                description: "Maximum number of lines to keep in scrollback history (max: 100,000; 0 disables scrolling).",
6232                field: Box::new(SettingField {
6233                    json_path: Some("terminal.max_scroll_history_lines"),
6234                    pick: |settings_content| {
6235                        settings_content
6236                            .terminal
6237                            .as_ref()?
6238                            .max_scroll_history_lines
6239                            .as_ref()
6240                    },
6241                    write: |settings_content, value| {
6242                        settings_content
6243                            .terminal
6244                            .get_or_insert_default()
6245                            .max_scroll_history_lines = value;
6246                    },
6247                }),
6248                metadata: None,
6249                files: USER,
6250            }),
6251            SettingsPageItem::SettingItem(SettingItem {
6252                title: "Scroll Multiplier",
6253                description: "The multiplier for scrolling in the terminal with the mouse wheel",
6254                field: Box::new(SettingField {
6255                    json_path: Some("terminal.scroll_multiplier"),
6256                    pick: |settings_content| {
6257                        settings_content
6258                            .terminal
6259                            .as_ref()?
6260                            .scroll_multiplier
6261                            .as_ref()
6262                    },
6263                    write: |settings_content, value| {
6264                        settings_content
6265                            .terminal
6266                            .get_or_insert_default()
6267                            .scroll_multiplier = value;
6268                    },
6269                }),
6270                metadata: None,
6271                files: USER,
6272            }),
6273        ]
6274    }
6275
6276    fn toolbar_section() -> [SettingsPageItem; 2] {
6277        [
6278            SettingsPageItem::SectionHeader("Toolbar"),
6279            SettingsPageItem::SettingItem(SettingItem {
6280                title: "Breadcrumbs",
6281                description: "Display the terminal title in breadcrumbs inside the terminal pane.",
6282                field: Box::new(SettingField {
6283                    json_path: Some("terminal.toolbar.breadcrumbs"),
6284                    pick: |settings_content| {
6285                        settings_content
6286                            .terminal
6287                            .as_ref()?
6288                            .toolbar
6289                            .as_ref()?
6290                            .breadcrumbs
6291                            .as_ref()
6292                    },
6293                    write: |settings_content, value| {
6294                        settings_content
6295                            .terminal
6296                            .get_or_insert_default()
6297                            .toolbar
6298                            .get_or_insert_default()
6299                            .breadcrumbs = value;
6300                    },
6301                }),
6302                metadata: None,
6303                files: USER,
6304            }),
6305        ]
6306    }
6307
6308    fn scrollbar_section() -> [SettingsPageItem; 2] {
6309        [
6310            SettingsPageItem::SectionHeader("Scrollbar"),
6311            SettingsPageItem::SettingItem(SettingItem {
6312                title: "Show Scrollbar",
6313                description: "When to show the scrollbar in the terminal.",
6314                field: Box::new(SettingField {
6315                    json_path: Some("terminal.scrollbar.show"),
6316                    pick: |settings_content| {
6317                        show_scrollbar_or_editor(settings_content, |settings_content| {
6318                            settings_content
6319                                .terminal
6320                                .as_ref()?
6321                                .scrollbar
6322                                .as_ref()?
6323                                .show
6324                                .as_ref()
6325                        })
6326                    },
6327                    write: |settings_content, value| {
6328                        settings_content
6329                            .terminal
6330                            .get_or_insert_default()
6331                            .scrollbar
6332                            .get_or_insert_default()
6333                            .show = value;
6334                    },
6335                }),
6336                metadata: None,
6337                files: USER,
6338            }),
6339        ]
6340    }
6341
6342    SettingsPage {
6343        title: "Terminal",
6344        items: concat_sections![
6345            environment_section(),
6346            font_section(),
6347            display_settings_section(),
6348            behavior_settings_section(),
6349            layout_settings_section(),
6350            advanced_settings_section(),
6351            toolbar_section(),
6352            scrollbar_section(),
6353        ],
6354    }
6355}
6356
6357fn version_control_page() -> SettingsPage {
6358    fn git_integration_section() -> [SettingsPageItem; 2] {
6359        [
6360            SettingsPageItem::SectionHeader("Git Integration"),
6361            SettingsPageItem::DynamicItem(DynamicItem {
6362                discriminant: SettingItem {
6363                    files: USER,
6364                    title: "Disable Git Integration",
6365                    description: "Disable all Git integration features in Zed.",
6366                    field: Box::new(SettingField::<bool> {
6367                        json_path: Some("git.disable_git"),
6368                        pick: |settings_content| {
6369                            settings_content
6370                                .git
6371                                .as_ref()?
6372                                .enabled
6373                                .as_ref()?
6374                                .disable_git
6375                                .as_ref()
6376                        },
6377                        write: |settings_content, value| {
6378                            settings_content
6379                                .git
6380                                .get_or_insert_default()
6381                                .enabled
6382                                .get_or_insert_default()
6383                                .disable_git = value;
6384                        },
6385                    }),
6386                    metadata: None,
6387                },
6388                pick_discriminant: |settings_content| {
6389                    let disabled = settings_content
6390                        .git
6391                        .as_ref()?
6392                        .enabled
6393                        .as_ref()?
6394                        .disable_git
6395                        .unwrap_or(false);
6396                    Some(if disabled { 0 } else { 1 })
6397                },
6398                fields: vec![
6399                    vec![],
6400                    vec![
6401                        SettingItem {
6402                            files: USER,
6403                            title: "Enable Git Status",
6404                            description: "Show Git status information in the editor.",
6405                            field: Box::new(SettingField::<bool> {
6406                                json_path: Some("git.enable_status"),
6407                                pick: |settings_content| {
6408                                    settings_content
6409                                        .git
6410                                        .as_ref()?
6411                                        .enabled
6412                                        .as_ref()?
6413                                        .enable_status
6414                                        .as_ref()
6415                                },
6416                                write: |settings_content, value| {
6417                                    settings_content
6418                                        .git
6419                                        .get_or_insert_default()
6420                                        .enabled
6421                                        .get_or_insert_default()
6422                                        .enable_status = value;
6423                                },
6424                            }),
6425                            metadata: None,
6426                        },
6427                        SettingItem {
6428                            files: USER,
6429                            title: "Enable Git Diff",
6430                            description: "Show Git diff information in the editor.",
6431                            field: Box::new(SettingField::<bool> {
6432                                json_path: Some("git.enable_diff"),
6433                                pick: |settings_content| {
6434                                    settings_content
6435                                        .git
6436                                        .as_ref()?
6437                                        .enabled
6438                                        .as_ref()?
6439                                        .enable_diff
6440                                        .as_ref()
6441                                },
6442                                write: |settings_content, value| {
6443                                    settings_content
6444                                        .git
6445                                        .get_or_insert_default()
6446                                        .enabled
6447                                        .get_or_insert_default()
6448                                        .enable_diff = value;
6449                                },
6450                            }),
6451                            metadata: None,
6452                        },
6453                    ],
6454                ],
6455            }),
6456        ]
6457    }
6458
6459    fn git_gutter_section() -> [SettingsPageItem; 3] {
6460        [
6461            SettingsPageItem::SectionHeader("Git Gutter"),
6462            SettingsPageItem::SettingItem(SettingItem {
6463                title: "Visibility",
6464                description: "Control whether Git status is shown in the editor's gutter.",
6465                field: Box::new(SettingField {
6466                    json_path: Some("git.git_gutter"),
6467                    pick: |settings_content| settings_content.git.as_ref()?.git_gutter.as_ref(),
6468                    write: |settings_content, value| {
6469                        settings_content.git.get_or_insert_default().git_gutter = value;
6470                    },
6471                }),
6472                metadata: None,
6473                files: USER,
6474            }),
6475            // todo(settings_ui): Figure out the right default for this value in default.json
6476            SettingsPageItem::SettingItem(SettingItem {
6477                title: "Debounce",
6478                description: "Debounce threshold in milliseconds after which changes are reflected in the Git gutter.",
6479                field: Box::new(SettingField {
6480                    json_path: Some("git.gutter_debounce"),
6481                    pick: |settings_content| {
6482                        settings_content.git.as_ref()?.gutter_debounce.as_ref()
6483                    },
6484                    write: |settings_content, value| {
6485                        settings_content.git.get_or_insert_default().gutter_debounce = value;
6486                    },
6487                }),
6488                metadata: None,
6489                files: USER,
6490            }),
6491        ]
6492    }
6493
6494    fn inline_git_blame_section() -> [SettingsPageItem; 6] {
6495        [
6496            SettingsPageItem::SectionHeader("Inline Git Blame"),
6497            SettingsPageItem::SettingItem(SettingItem {
6498                title: "Enabled",
6499                description: "Whether or not to show Git blame data inline in the currently focused line.",
6500                field: Box::new(SettingField {
6501                    json_path: Some("git.inline_blame.enabled"),
6502                    pick: |settings_content| {
6503                        settings_content
6504                            .git
6505                            .as_ref()?
6506                            .inline_blame
6507                            .as_ref()?
6508                            .enabled
6509                            .as_ref()
6510                    },
6511                    write: |settings_content, value| {
6512                        settings_content
6513                            .git
6514                            .get_or_insert_default()
6515                            .inline_blame
6516                            .get_or_insert_default()
6517                            .enabled = value;
6518                    },
6519                }),
6520                metadata: None,
6521                files: USER,
6522            }),
6523            SettingsPageItem::SettingItem(SettingItem {
6524                title: "Delay",
6525                description: "The delay after which the inline blame information is shown.",
6526                field: Box::new(SettingField {
6527                    json_path: Some("git.inline_blame.delay_ms"),
6528                    pick: |settings_content| {
6529                        settings_content
6530                            .git
6531                            .as_ref()?
6532                            .inline_blame
6533                            .as_ref()?
6534                            .delay_ms
6535                            .as_ref()
6536                    },
6537                    write: |settings_content, value| {
6538                        settings_content
6539                            .git
6540                            .get_or_insert_default()
6541                            .inline_blame
6542                            .get_or_insert_default()
6543                            .delay_ms = value;
6544                    },
6545                }),
6546                metadata: None,
6547                files: USER,
6548            }),
6549            SettingsPageItem::SettingItem(SettingItem {
6550                title: "Padding",
6551                description: "Padding between the end of the source line and the start of the inline blame in columns.",
6552                field: Box::new(SettingField {
6553                    json_path: Some("git.inline_blame.padding"),
6554                    pick: |settings_content| {
6555                        settings_content
6556                            .git
6557                            .as_ref()?
6558                            .inline_blame
6559                            .as_ref()?
6560                            .padding
6561                            .as_ref()
6562                    },
6563                    write: |settings_content, value| {
6564                        settings_content
6565                            .git
6566                            .get_or_insert_default()
6567                            .inline_blame
6568                            .get_or_insert_default()
6569                            .padding = value;
6570                    },
6571                }),
6572                metadata: None,
6573                files: USER,
6574            }),
6575            SettingsPageItem::SettingItem(SettingItem {
6576                title: "Minimum Column",
6577                description: "The minimum column number at which to show the inline blame information.",
6578                field: Box::new(SettingField {
6579                    json_path: Some("git.inline_blame.min_column"),
6580                    pick: |settings_content| {
6581                        settings_content
6582                            .git
6583                            .as_ref()?
6584                            .inline_blame
6585                            .as_ref()?
6586                            .min_column
6587                            .as_ref()
6588                    },
6589                    write: |settings_content, value| {
6590                        settings_content
6591                            .git
6592                            .get_or_insert_default()
6593                            .inline_blame
6594                            .get_or_insert_default()
6595                            .min_column = value;
6596                    },
6597                }),
6598                metadata: None,
6599                files: USER,
6600            }),
6601            SettingsPageItem::SettingItem(SettingItem {
6602                title: "Show Commit Summary",
6603                description: "Show commit summary as part of the inline blame.",
6604                field: Box::new(SettingField {
6605                    json_path: Some("git.inline_blame.show_commit_summary"),
6606                    pick: |settings_content| {
6607                        settings_content
6608                            .git
6609                            .as_ref()?
6610                            .inline_blame
6611                            .as_ref()?
6612                            .show_commit_summary
6613                            .as_ref()
6614                    },
6615                    write: |settings_content, value| {
6616                        settings_content
6617                            .git
6618                            .get_or_insert_default()
6619                            .inline_blame
6620                            .get_or_insert_default()
6621                            .show_commit_summary = value;
6622                    },
6623                }),
6624                metadata: None,
6625                files: USER,
6626            }),
6627        ]
6628    }
6629
6630    fn git_blame_view_section() -> [SettingsPageItem; 2] {
6631        [
6632            SettingsPageItem::SectionHeader("Git Blame View"),
6633            SettingsPageItem::SettingItem(SettingItem {
6634                title: "Show Avatar",
6635                description: "Show the avatar of the author of the commit.",
6636                field: Box::new(SettingField {
6637                    json_path: Some("git.blame.show_avatar"),
6638                    pick: |settings_content| {
6639                        settings_content
6640                            .git
6641                            .as_ref()?
6642                            .blame
6643                            .as_ref()?
6644                            .show_avatar
6645                            .as_ref()
6646                    },
6647                    write: |settings_content, value| {
6648                        settings_content
6649                            .git
6650                            .get_or_insert_default()
6651                            .blame
6652                            .get_or_insert_default()
6653                            .show_avatar = value;
6654                    },
6655                }),
6656                metadata: None,
6657                files: USER,
6658            }),
6659        ]
6660    }
6661
6662    fn branch_picker_section() -> [SettingsPageItem; 2] {
6663        [
6664            SettingsPageItem::SectionHeader("Branch Picker"),
6665            SettingsPageItem::SettingItem(SettingItem {
6666                title: "Show Author Name",
6667                description: "Show author name as part of the commit information in branch picker.",
6668                field: Box::new(SettingField {
6669                    json_path: Some("git.branch_picker.show_author_name"),
6670                    pick: |settings_content| {
6671                        settings_content
6672                            .git
6673                            .as_ref()?
6674                            .branch_picker
6675                            .as_ref()?
6676                            .show_author_name
6677                            .as_ref()
6678                    },
6679                    write: |settings_content, value| {
6680                        settings_content
6681                            .git
6682                            .get_or_insert_default()
6683                            .branch_picker
6684                            .get_or_insert_default()
6685                            .show_author_name = value;
6686                    },
6687                }),
6688                metadata: None,
6689                files: USER,
6690            }),
6691        ]
6692    }
6693
6694    fn git_hunks_section() -> [SettingsPageItem; 3] {
6695        [
6696            SettingsPageItem::SectionHeader("Git Hunks"),
6697            SettingsPageItem::SettingItem(SettingItem {
6698                title: "Hunk Style",
6699                description: "How Git hunks are displayed visually in the editor.",
6700                field: Box::new(SettingField {
6701                    json_path: Some("git.hunk_style"),
6702                    pick: |settings_content| settings_content.git.as_ref()?.hunk_style.as_ref(),
6703                    write: |settings_content, value| {
6704                        settings_content.git.get_or_insert_default().hunk_style = value;
6705                    },
6706                }),
6707                metadata: None,
6708                files: USER,
6709            }),
6710            SettingsPageItem::SettingItem(SettingItem {
6711                title: "Path Style",
6712                description: "Should the name or path be displayed first in the git view.",
6713                field: Box::new(SettingField {
6714                    json_path: Some("git.path_style"),
6715                    pick: |settings_content| settings_content.git.as_ref()?.path_style.as_ref(),
6716                    write: |settings_content, value| {
6717                        settings_content.git.get_or_insert_default().path_style = value;
6718                    },
6719                }),
6720                metadata: None,
6721                files: USER,
6722            }),
6723        ]
6724    }
6725
6726    SettingsPage {
6727        title: "Version Control",
6728        items: concat_sections![
6729            git_integration_section(),
6730            git_gutter_section(),
6731            inline_git_blame_section(),
6732            git_blame_view_section(),
6733            branch_picker_section(),
6734            git_hunks_section(),
6735        ],
6736    }
6737}
6738
6739fn collaboration_page() -> SettingsPage {
6740    fn calls_section() -> [SettingsPageItem; 3] {
6741        [
6742            SettingsPageItem::SectionHeader("Calls"),
6743            SettingsPageItem::SettingItem(SettingItem {
6744                title: "Mute On Join",
6745                description: "Whether the microphone should be muted when joining a channel or a call.",
6746                field: Box::new(SettingField {
6747                    json_path: Some("calls.mute_on_join"),
6748                    pick: |settings_content| settings_content.calls.as_ref()?.mute_on_join.as_ref(),
6749                    write: |settings_content, value| {
6750                        settings_content.calls.get_or_insert_default().mute_on_join = value;
6751                    },
6752                }),
6753                metadata: None,
6754                files: USER,
6755            }),
6756            SettingsPageItem::SettingItem(SettingItem {
6757                title: "Share On Join",
6758                description: "Whether your current project should be shared when joining an empty channel.",
6759                field: Box::new(SettingField {
6760                    json_path: Some("calls.share_on_join"),
6761                    pick: |settings_content| {
6762                        settings_content.calls.as_ref()?.share_on_join.as_ref()
6763                    },
6764                    write: |settings_content, value| {
6765                        settings_content.calls.get_or_insert_default().share_on_join = value;
6766                    },
6767                }),
6768                metadata: None,
6769                files: USER,
6770            }),
6771        ]
6772    }
6773
6774    fn experimental_section() -> [SettingsPageItem; 9] {
6775        [
6776            SettingsPageItem::SectionHeader("Experimental"),
6777            SettingsPageItem::SettingItem(SettingItem {
6778                title: "Rodio Audio",
6779                description: "Opt into the new audio system.",
6780                field: Box::new(SettingField {
6781                    json_path: Some("audio.experimental.rodio_audio"),
6782                    pick: |settings_content| settings_content.audio.as_ref()?.rodio_audio.as_ref(),
6783                    write: |settings_content, value| {
6784                        settings_content.audio.get_or_insert_default().rodio_audio = value;
6785                    },
6786                }),
6787                metadata: None,
6788                files: USER,
6789            }),
6790            SettingsPageItem::SettingItem(SettingItem {
6791                title: "Auto Microphone Volume",
6792                description: "Automatically adjust microphone volume (requires rodio audio).",
6793                field: Box::new(SettingField {
6794                    json_path: Some("audio.experimental.auto_microphone_volume"),
6795                    pick: |settings_content| {
6796                        settings_content
6797                            .audio
6798                            .as_ref()?
6799                            .auto_microphone_volume
6800                            .as_ref()
6801                    },
6802                    write: |settings_content, value| {
6803                        settings_content
6804                            .audio
6805                            .get_or_insert_default()
6806                            .auto_microphone_volume = value;
6807                    },
6808                }),
6809                metadata: None,
6810                files: USER,
6811            }),
6812            SettingsPageItem::SettingItem(SettingItem {
6813                title: "Auto Speaker Volume",
6814                description: "Automatically adjust volume of other call members (requires rodio audio).",
6815                field: Box::new(SettingField {
6816                    json_path: Some("audio.experimental.auto_speaker_volume"),
6817                    pick: |settings_content| {
6818                        settings_content
6819                            .audio
6820                            .as_ref()?
6821                            .auto_speaker_volume
6822                            .as_ref()
6823                    },
6824                    write: |settings_content, value| {
6825                        settings_content
6826                            .audio
6827                            .get_or_insert_default()
6828                            .auto_speaker_volume = value;
6829                    },
6830                }),
6831                metadata: None,
6832                files: USER,
6833            }),
6834            SettingsPageItem::SettingItem(SettingItem {
6835                title: "Denoise",
6836                description: "Remove background noises (requires rodio audio).",
6837                field: Box::new(SettingField {
6838                    json_path: Some("audio.experimental.denoise"),
6839                    pick: |settings_content| settings_content.audio.as_ref()?.denoise.as_ref(),
6840                    write: |settings_content, value| {
6841                        settings_content.audio.get_or_insert_default().denoise = value;
6842                    },
6843                }),
6844                metadata: None,
6845                files: USER,
6846            }),
6847            SettingsPageItem::SettingItem(SettingItem {
6848                title: "Legacy Audio Compatible",
6849                description: "Use audio parameters compatible with previous versions (requires rodio audio).",
6850                field: Box::new(SettingField {
6851                    json_path: Some("audio.experimental.legacy_audio_compatible"),
6852                    pick: |settings_content| {
6853                        settings_content
6854                            .audio
6855                            .as_ref()?
6856                            .legacy_audio_compatible
6857                            .as_ref()
6858                    },
6859                    write: |settings_content, value| {
6860                        settings_content
6861                            .audio
6862                            .get_or_insert_default()
6863                            .legacy_audio_compatible = value;
6864                    },
6865                }),
6866                metadata: None,
6867                files: USER,
6868            }),
6869            SettingsPageItem::ActionLink(ActionLink {
6870                title: "Test Audio".into(),
6871                description: Some("Test your microphone and speaker setup".into()),
6872                button_text: "Test Audio".into(),
6873                on_click: Arc::new(|_settings_window, window, cx| {
6874                    open_audio_test_window(window, cx);
6875                }),
6876                files: USER,
6877            }),
6878            SettingsPageItem::SettingItem(SettingItem {
6879                title: "Output Audio Device",
6880                description: "Select output audio device",
6881                field: Box::new(SettingField {
6882                    json_path: Some("audio.experimental.output_audio_device"),
6883                    pick: |settings_content| {
6884                        settings_content
6885                            .audio
6886                            .as_ref()?
6887                            .output_audio_device
6888                            .as_ref()
6889                            .or(DEFAULT_EMPTY_AUDIO_OUTPUT)
6890                    },
6891                    write: |settings_content, value| {
6892                        settings_content
6893                            .audio
6894                            .get_or_insert_default()
6895                            .output_audio_device = value;
6896                    },
6897                }),
6898                metadata: None,
6899                files: USER,
6900            }),
6901            SettingsPageItem::SettingItem(SettingItem {
6902                title: "Input Audio Device",
6903                description: "Select input audio device",
6904                field: Box::new(SettingField {
6905                    json_path: Some("audio.experimental.input_audio_device"),
6906                    pick: |settings_content| {
6907                        settings_content
6908                            .audio
6909                            .as_ref()?
6910                            .input_audio_device
6911                            .as_ref()
6912                            .or(DEFAULT_EMPTY_AUDIO_INPUT)
6913                    },
6914                    write: |settings_content, value| {
6915                        settings_content
6916                            .audio
6917                            .get_or_insert_default()
6918                            .input_audio_device = value;
6919                    },
6920                }),
6921                metadata: None,
6922                files: USER,
6923            }),
6924        ]
6925    }
6926
6927    SettingsPage {
6928        title: "Collaboration",
6929        items: concat_sections![calls_section(), experimental_section()],
6930    }
6931}
6932
6933fn ai_page() -> SettingsPage {
6934    fn general_section() -> [SettingsPageItem; 2] {
6935        [
6936            SettingsPageItem::SectionHeader("General"),
6937            SettingsPageItem::SettingItem(SettingItem {
6938                title: "Disable AI",
6939                description: "Whether to disable all AI features in Zed.",
6940                field: Box::new(SettingField {
6941                    json_path: Some("disable_ai"),
6942                    pick: |settings_content| settings_content.project.disable_ai.as_ref(),
6943                    write: |settings_content, value| {
6944                        settings_content.project.disable_ai = value;
6945                    },
6946                }),
6947                metadata: None,
6948                files: USER | PROJECT,
6949            }),
6950        ]
6951    }
6952
6953    fn agent_configuration_section() -> [SettingsPageItem; 12] {
6954        [
6955            SettingsPageItem::SectionHeader("Agent Configuration"),
6956            SettingsPageItem::SubPageLink(SubPageLink {
6957                title: "Tool Permissions".into(),
6958                r#type: Default::default(),
6959                json_path: Some("agent.tool_permissions"),
6960                description: Some("Set up regex patterns to auto-allow, auto-deny, or always request confirmation, for specific tool inputs.".into()),
6961                in_json: true,
6962                files: USER,
6963                render: render_tool_permissions_setup_page,
6964            }),
6965            SettingsPageItem::SettingItem(SettingItem {
6966                title: "Single File Review",
6967                description: "When enabled, agent edits will also be displayed in single-file buffers for review.",
6968                field: Box::new(SettingField {
6969                    json_path: Some("agent.single_file_review"),
6970                    pick: |settings_content| {
6971                        settings_content.agent.as_ref()?.single_file_review.as_ref()
6972                    },
6973                    write: |settings_content, value| {
6974                        settings_content
6975                            .agent
6976                            .get_or_insert_default()
6977                            .single_file_review = value;
6978                    },
6979                }),
6980                metadata: None,
6981                files: USER,
6982            }),
6983            SettingsPageItem::SettingItem(SettingItem {
6984                title: "Enable Feedback",
6985                description: "Show voting thumbs up/down icon buttons for feedback on agent edits.",
6986                field: Box::new(SettingField {
6987                    json_path: Some("agent.enable_feedback"),
6988                    pick: |settings_content| {
6989                        settings_content.agent.as_ref()?.enable_feedback.as_ref()
6990                    },
6991                    write: |settings_content, value| {
6992                        settings_content
6993                            .agent
6994                            .get_or_insert_default()
6995                            .enable_feedback = value;
6996                    },
6997                }),
6998                metadata: None,
6999                files: USER,
7000            }),
7001            SettingsPageItem::SettingItem(SettingItem {
7002                title: "Notify When Agent Waiting",
7003                description: "Where to show notifications when the agent has completed its response or needs confirmation before running a tool action.",
7004                field: Box::new(SettingField {
7005                    json_path: Some("agent.notify_when_agent_waiting"),
7006                    pick: |settings_content| {
7007                        settings_content
7008                            .agent
7009                            .as_ref()?
7010                            .notify_when_agent_waiting
7011                            .as_ref()
7012                    },
7013                    write: |settings_content, value| {
7014                        settings_content
7015                            .agent
7016                            .get_or_insert_default()
7017                            .notify_when_agent_waiting = value;
7018                    },
7019                }),
7020                metadata: None,
7021                files: USER,
7022            }),
7023            SettingsPageItem::SettingItem(SettingItem {
7024                title: "Play Sound When Agent Done",
7025                description: "Whether to play a sound when the agent has either completed its response, or needs user input.",
7026                field: Box::new(SettingField {
7027                    json_path: Some("agent.play_sound_when_agent_done"),
7028                    pick: |settings_content| {
7029                        settings_content
7030                            .agent
7031                            .as_ref()?
7032                            .play_sound_when_agent_done
7033                            .as_ref()
7034                    },
7035                    write: |settings_content, value| {
7036                        settings_content
7037                            .agent
7038                            .get_or_insert_default()
7039                            .play_sound_when_agent_done = value;
7040                    },
7041                }),
7042                metadata: None,
7043                files: USER,
7044            }),
7045            SettingsPageItem::SettingItem(SettingItem {
7046                title: "Expand Edit Card",
7047                description: "Whether to have edit cards in the agent panel expanded, showing a Preview of the diff.",
7048                field: Box::new(SettingField {
7049                    json_path: Some("agent.expand_edit_card"),
7050                    pick: |settings_content| {
7051                        settings_content.agent.as_ref()?.expand_edit_card.as_ref()
7052                    },
7053                    write: |settings_content, value| {
7054                        settings_content
7055                            .agent
7056                            .get_or_insert_default()
7057                            .expand_edit_card = value;
7058                    },
7059                }),
7060                metadata: None,
7061                files: USER,
7062            }),
7063            SettingsPageItem::SettingItem(SettingItem {
7064                title: "Expand Terminal Card",
7065                description: "Whether to have terminal cards in the agent panel expanded, showing the whole command output.",
7066                field: Box::new(SettingField {
7067                    json_path: Some("agent.expand_terminal_card"),
7068                    pick: |settings_content| {
7069                        settings_content
7070                            .agent
7071                            .as_ref()?
7072                            .expand_terminal_card
7073                            .as_ref()
7074                    },
7075                    write: |settings_content, value| {
7076                        settings_content
7077                            .agent
7078                            .get_or_insert_default()
7079                            .expand_terminal_card = value;
7080                    },
7081                }),
7082                metadata: None,
7083                files: USER,
7084            }),
7085            SettingsPageItem::SettingItem(SettingItem {
7086                title: "Cancel Generation On Terminal Stop",
7087                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.",
7088                field: Box::new(SettingField {
7089                    json_path: Some("agent.cancel_generation_on_terminal_stop"),
7090                    pick: |settings_content| {
7091                        settings_content
7092                            .agent
7093                            .as_ref()?
7094                            .cancel_generation_on_terminal_stop
7095                            .as_ref()
7096                    },
7097                    write: |settings_content, value| {
7098                        settings_content
7099                            .agent
7100                            .get_or_insert_default()
7101                            .cancel_generation_on_terminal_stop = value;
7102                    },
7103                }),
7104                metadata: None,
7105                files: USER,
7106            }),
7107            SettingsPageItem::SettingItem(SettingItem {
7108                title: "Use Modifier To Send",
7109                description: "Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages.",
7110                field: Box::new(SettingField {
7111                    json_path: Some("agent.use_modifier_to_send"),
7112                    pick: |settings_content| {
7113                        settings_content
7114                            .agent
7115                            .as_ref()?
7116                            .use_modifier_to_send
7117                            .as_ref()
7118                    },
7119                    write: |settings_content, value| {
7120                        settings_content
7121                            .agent
7122                            .get_or_insert_default()
7123                            .use_modifier_to_send = value;
7124                    },
7125                }),
7126                metadata: None,
7127                files: USER,
7128            }),
7129            SettingsPageItem::SettingItem(SettingItem {
7130                title: "Message Editor Min Lines",
7131                description: "Minimum number of lines to display in the agent message editor.",
7132                field: Box::new(SettingField {
7133                    json_path: Some("agent.message_editor_min_lines"),
7134                    pick: |settings_content| {
7135                        settings_content
7136                            .agent
7137                            .as_ref()?
7138                            .message_editor_min_lines
7139                            .as_ref()
7140                    },
7141                    write: |settings_content, value| {
7142                        settings_content
7143                            .agent
7144                            .get_or_insert_default()
7145                            .message_editor_min_lines = value;
7146                    },
7147                }),
7148                metadata: None,
7149                files: USER,
7150            }),
7151            SettingsPageItem::SettingItem(SettingItem {
7152                title: "Show Turn Stats",
7153                description: "Whether to show turn statistics like elapsed time during generation and final turn duration.",
7154                field: Box::new(SettingField {
7155                    json_path: Some("agent.show_turn_stats"),
7156                    pick: |settings_content| {
7157                        settings_content.agent.as_ref()?.show_turn_stats.as_ref()
7158                    },
7159                    write: |settings_content, value| {
7160                        settings_content
7161                            .agent
7162                            .get_or_insert_default()
7163                            .show_turn_stats = value;
7164                    },
7165                }),
7166                metadata: None,
7167                files: USER,
7168            }),
7169        ]
7170    }
7171
7172    fn context_servers_section() -> [SettingsPageItem; 2] {
7173        [
7174            SettingsPageItem::SectionHeader("Context Servers"),
7175            SettingsPageItem::SettingItem(SettingItem {
7176                title: "Context Server Timeout",
7177                description: "Default timeout in seconds for context server tool calls. Can be overridden per-server in context_servers configuration.",
7178                field: Box::new(SettingField {
7179                    json_path: Some("context_server_timeout"),
7180                    pick: |settings_content| {
7181                        settings_content.project.context_server_timeout.as_ref()
7182                    },
7183                    write: |settings_content, value| {
7184                        settings_content.project.context_server_timeout = value;
7185                    },
7186                }),
7187                metadata: None,
7188                files: USER | PROJECT,
7189            }),
7190        ]
7191    }
7192
7193    fn edit_prediction_display_sub_section() -> [SettingsPageItem; 2] {
7194        [
7195            SettingsPageItem::SettingItem(SettingItem {
7196                title: "Display Mode",
7197                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.",
7198                field: Box::new(SettingField {
7199                    json_path: Some("edit_prediction.display_mode"),
7200                    pick: |settings_content| {
7201                        settings_content
7202                            .project
7203                            .all_languages
7204                            .edit_predictions
7205                            .as_ref()?
7206                            .mode
7207                            .as_ref()
7208                    },
7209                    write: |settings_content, value| {
7210                        settings_content
7211                            .project
7212                            .all_languages
7213                            .edit_predictions
7214                            .get_or_insert_default()
7215                            .mode = value;
7216                    },
7217                }),
7218                metadata: None,
7219                files: USER,
7220            }),
7221            SettingsPageItem::SettingItem(SettingItem {
7222                title: "Display In Text Threads",
7223                description: "Whether edit predictions are enabled when editing text threads in the agent panel.",
7224                field: Box::new(SettingField {
7225                    json_path: Some("edit_prediction.in_text_threads"),
7226                    pick: |settings_content| {
7227                        settings_content
7228                            .project
7229                            .all_languages
7230                            .edit_predictions
7231                            .as_ref()?
7232                            .enabled_in_text_threads
7233                            .as_ref()
7234                    },
7235                    write: |settings_content, value| {
7236                        settings_content
7237                            .project
7238                            .all_languages
7239                            .edit_predictions
7240                            .get_or_insert_default()
7241                            .enabled_in_text_threads = value;
7242                    },
7243                }),
7244                metadata: None,
7245                files: USER,
7246            }),
7247        ]
7248    }
7249
7250    SettingsPage {
7251        title: "AI",
7252        items: concat_sections![
7253            general_section(),
7254            agent_configuration_section(),
7255            context_servers_section(),
7256            edit_prediction_language_settings_section(),
7257            edit_prediction_display_sub_section()
7258        ],
7259    }
7260}
7261
7262fn network_page() -> SettingsPage {
7263    fn network_section() -> [SettingsPageItem; 3] {
7264        [
7265            SettingsPageItem::SectionHeader("Network"),
7266            SettingsPageItem::SettingItem(SettingItem {
7267                title: "Proxy",
7268                description: "The proxy to use for network requests.",
7269                field: Box::new(SettingField {
7270                    json_path: Some("proxy"),
7271                    pick: |settings_content| settings_content.proxy.as_ref(),
7272                    write: |settings_content, value| {
7273                        settings_content.proxy = value;
7274                    },
7275                }),
7276                metadata: Some(Box::new(SettingsFieldMetadata {
7277                    placeholder: Some("socks5h://localhost:10808"),
7278                    ..Default::default()
7279                })),
7280                files: USER,
7281            }),
7282            SettingsPageItem::SettingItem(SettingItem {
7283                title: "Server URL",
7284                description: "The URL of the Zed server to connect to.",
7285                field: Box::new(SettingField {
7286                    json_path: Some("server_url"),
7287                    pick: |settings_content| settings_content.server_url.as_ref(),
7288                    write: |settings_content, value| {
7289                        settings_content.server_url = value;
7290                    },
7291                }),
7292                metadata: Some(Box::new(SettingsFieldMetadata {
7293                    placeholder: Some("https://zed.dev"),
7294                    ..Default::default()
7295                })),
7296                files: USER,
7297            }),
7298        ]
7299    }
7300
7301    SettingsPage {
7302        title: "Network",
7303        items: concat_sections![network_section()],
7304    }
7305}
7306
7307fn language_settings_field<T>(
7308    settings_content: &SettingsContent,
7309    get_language_setting_field: fn(&LanguageSettingsContent) -> Option<&T>,
7310) -> Option<&T> {
7311    let all_languages = &settings_content.project.all_languages;
7312
7313    active_language()
7314        .and_then(|current_language_name| {
7315            all_languages
7316                .languages
7317                .0
7318                .get(current_language_name.as_ref())
7319        })
7320        .and_then(get_language_setting_field)
7321        .or_else(|| get_language_setting_field(&all_languages.defaults))
7322}
7323
7324fn language_settings_field_mut<T>(
7325    settings_content: &mut SettingsContent,
7326    value: Option<T>,
7327    write: fn(&mut LanguageSettingsContent, Option<T>),
7328) {
7329    let all_languages = &mut settings_content.project.all_languages;
7330    let language_content = if let Some(current_language) = active_language() {
7331        all_languages
7332            .languages
7333            .0
7334            .entry(current_language.to_string())
7335            .or_default()
7336    } else {
7337        &mut all_languages.defaults
7338    };
7339    write(language_content, value);
7340}
7341
7342fn language_settings_data() -> Box<[SettingsPageItem]> {
7343    fn indentation_section() -> [SettingsPageItem; 5] {
7344        [
7345            SettingsPageItem::SectionHeader("Indentation"),
7346            SettingsPageItem::SettingItem(SettingItem {
7347                title: "Tab Size",
7348                description: "How many columns a tab should occupy.",
7349                field: Box::new(SettingField {
7350                    json_path: Some("languages.$(language).tab_size"), // TODO(cameron): not JQ syntax because not URL-safe
7351                    pick: |settings_content| {
7352                        language_settings_field(settings_content, |language| {
7353                            language.tab_size.as_ref()
7354                        })
7355                    },
7356                    write: |settings_content, value| {
7357                        language_settings_field_mut(settings_content, value, |language, value| {
7358                            language.tab_size = value;
7359                        })
7360                    },
7361                }),
7362                metadata: None,
7363                files: USER | PROJECT,
7364            }),
7365            SettingsPageItem::SettingItem(SettingItem {
7366                title: "Hard Tabs",
7367                description: "Whether to indent lines using tab characters, as opposed to multiple spaces.",
7368                field: Box::new(SettingField {
7369                    json_path: Some("languages.$(language).hard_tabs"),
7370                    pick: |settings_content| {
7371                        language_settings_field(settings_content, |language| {
7372                            language.hard_tabs.as_ref()
7373                        })
7374                    },
7375                    write: |settings_content, value| {
7376                        language_settings_field_mut(settings_content, value, |language, value| {
7377                            language.hard_tabs = value;
7378                        })
7379                    },
7380                }),
7381                metadata: None,
7382                files: USER | PROJECT,
7383            }),
7384            SettingsPageItem::SettingItem(SettingItem {
7385                title: "Auto Indent",
7386                description: "Whether indentation should be adjusted based on the context whilst typing.",
7387                field: Box::new(SettingField {
7388                    json_path: Some("languages.$(language).auto_indent"),
7389                    pick: |settings_content| {
7390                        language_settings_field(settings_content, |language| {
7391                            language.auto_indent.as_ref()
7392                        })
7393                    },
7394                    write: |settings_content, value| {
7395                        language_settings_field_mut(settings_content, value, |language, value| {
7396                            language.auto_indent = value;
7397                        })
7398                    },
7399                }),
7400                metadata: None,
7401                files: USER | PROJECT,
7402            }),
7403            SettingsPageItem::SettingItem(SettingItem {
7404                title: "Auto Indent On Paste",
7405                description: "Whether indentation of pasted content should be adjusted based on the context.",
7406                field: Box::new(SettingField {
7407                    json_path: Some("languages.$(language).auto_indent_on_paste"),
7408                    pick: |settings_content| {
7409                        language_settings_field(settings_content, |language| {
7410                            language.auto_indent_on_paste.as_ref()
7411                        })
7412                    },
7413                    write: |settings_content, value| {
7414                        language_settings_field_mut(settings_content, value, |language, value| {
7415                            language.auto_indent_on_paste = value;
7416                        })
7417                    },
7418                }),
7419                metadata: None,
7420                files: USER | PROJECT,
7421            }),
7422        ]
7423    }
7424
7425    fn wrapping_section() -> [SettingsPageItem; 6] {
7426        [
7427            SettingsPageItem::SectionHeader("Wrapping"),
7428            SettingsPageItem::SettingItem(SettingItem {
7429                title: "Soft Wrap",
7430                description: "How to soft-wrap long lines of text.",
7431                field: Box::new(SettingField {
7432                    json_path: Some("languages.$(language).soft_wrap"),
7433                    pick: |settings_content| {
7434                        language_settings_field(settings_content, |language| {
7435                            language.soft_wrap.as_ref()
7436                        })
7437                    },
7438                    write: |settings_content, value| {
7439                        language_settings_field_mut(settings_content, value, |language, value| {
7440                            language.soft_wrap = value;
7441                        })
7442                    },
7443                }),
7444                metadata: None,
7445                files: USER | PROJECT,
7446            }),
7447            SettingsPageItem::SettingItem(SettingItem {
7448                title: "Show Wrap Guides",
7449                description: "Show wrap guides in the editor.",
7450                field: Box::new(SettingField {
7451                    json_path: Some("languages.$(language).show_wrap_guides"),
7452                    pick: |settings_content| {
7453                        language_settings_field(settings_content, |language| {
7454                            language.show_wrap_guides.as_ref()
7455                        })
7456                    },
7457                    write: |settings_content, value| {
7458                        language_settings_field_mut(settings_content, value, |language, value| {
7459                            language.show_wrap_guides = value;
7460                        })
7461                    },
7462                }),
7463                metadata: None,
7464                files: USER | PROJECT,
7465            }),
7466            SettingsPageItem::SettingItem(SettingItem {
7467                title: "Preferred Line Length",
7468                description: "The column at which to soft-wrap lines, for buffers where soft-wrap is enabled.",
7469                field: Box::new(SettingField {
7470                    json_path: Some("languages.$(language).preferred_line_length"),
7471                    pick: |settings_content| {
7472                        language_settings_field(settings_content, |language| {
7473                            language.preferred_line_length.as_ref()
7474                        })
7475                    },
7476                    write: |settings_content, value| {
7477                        language_settings_field_mut(settings_content, value, |language, value| {
7478                            language.preferred_line_length = value;
7479                        })
7480                    },
7481                }),
7482                metadata: None,
7483                files: USER | PROJECT,
7484            }),
7485            SettingsPageItem::SettingItem(SettingItem {
7486                title: "Wrap Guides",
7487                description: "Character counts at which to show wrap guides in the editor.",
7488                field: Box::new(
7489                    SettingField {
7490                        json_path: Some("languages.$(language).wrap_guides"),
7491                        pick: |settings_content| {
7492                            language_settings_field(settings_content, |language| {
7493                                language.wrap_guides.as_ref()
7494                            })
7495                        },
7496                        write: |settings_content, value| {
7497                            language_settings_field_mut(
7498                                settings_content,
7499                                value,
7500                                |language, value| {
7501                                    language.wrap_guides = value;
7502                                },
7503                            )
7504                        },
7505                    }
7506                    .unimplemented(),
7507                ),
7508                metadata: None,
7509                files: USER | PROJECT,
7510            }),
7511            SettingsPageItem::SettingItem(SettingItem {
7512                title: "Allow Rewrap",
7513                description: "Controls where the `editor::rewrap` action is allowed for this language.",
7514                field: Box::new(SettingField {
7515                    json_path: Some("languages.$(language).allow_rewrap"),
7516                    pick: |settings_content| {
7517                        language_settings_field(settings_content, |language| {
7518                            language.allow_rewrap.as_ref()
7519                        })
7520                    },
7521                    write: |settings_content, value| {
7522                        language_settings_field_mut(settings_content, value, |language, value| {
7523                            language.allow_rewrap = value;
7524                        })
7525                    },
7526                }),
7527                metadata: None,
7528                files: USER | PROJECT,
7529            }),
7530        ]
7531    }
7532
7533    fn indent_guides_section() -> [SettingsPageItem; 6] {
7534        [
7535            SettingsPageItem::SectionHeader("Indent Guides"),
7536            SettingsPageItem::SettingItem(SettingItem {
7537                title: "Enabled",
7538                description: "Display indent guides in the editor.",
7539                field: Box::new(SettingField {
7540                    json_path: Some("languages.$(language).indent_guides.enabled"),
7541                    pick: |settings_content| {
7542                        language_settings_field(settings_content, |language| {
7543                            language
7544                                .indent_guides
7545                                .as_ref()
7546                                .and_then(|indent_guides| indent_guides.enabled.as_ref())
7547                        })
7548                    },
7549                    write: |settings_content, value| {
7550                        language_settings_field_mut(settings_content, value, |language, value| {
7551                            language.indent_guides.get_or_insert_default().enabled = value;
7552                        })
7553                    },
7554                }),
7555                metadata: None,
7556                files: USER | PROJECT,
7557            }),
7558            SettingsPageItem::SettingItem(SettingItem {
7559                title: "Line Width",
7560                description: "The width of the indent guides in pixels, between 1 and 10.",
7561                field: Box::new(SettingField {
7562                    json_path: Some("languages.$(language).indent_guides.line_width"),
7563                    pick: |settings_content| {
7564                        language_settings_field(settings_content, |language| {
7565                            language
7566                                .indent_guides
7567                                .as_ref()
7568                                .and_then(|indent_guides| indent_guides.line_width.as_ref())
7569                        })
7570                    },
7571                    write: |settings_content, value| {
7572                        language_settings_field_mut(settings_content, value, |language, value| {
7573                            language.indent_guides.get_or_insert_default().line_width = value;
7574                        })
7575                    },
7576                }),
7577                metadata: None,
7578                files: USER | PROJECT,
7579            }),
7580            SettingsPageItem::SettingItem(SettingItem {
7581                title: "Active Line Width",
7582                description: "The width of the active indent guide in pixels, between 1 and 10.",
7583                field: Box::new(SettingField {
7584                    json_path: Some("languages.$(language).indent_guides.active_line_width"),
7585                    pick: |settings_content| {
7586                        language_settings_field(settings_content, |language| {
7587                            language
7588                                .indent_guides
7589                                .as_ref()
7590                                .and_then(|indent_guides| indent_guides.active_line_width.as_ref())
7591                        })
7592                    },
7593                    write: |settings_content, value| {
7594                        language_settings_field_mut(settings_content, value, |language, value| {
7595                            language
7596                                .indent_guides
7597                                .get_or_insert_default()
7598                                .active_line_width = value;
7599                        })
7600                    },
7601                }),
7602                metadata: None,
7603                files: USER | PROJECT,
7604            }),
7605            SettingsPageItem::SettingItem(SettingItem {
7606                title: "Coloring",
7607                description: "Determines how indent guides are colored.",
7608                field: Box::new(SettingField {
7609                    json_path: Some("languages.$(language).indent_guides.coloring"),
7610                    pick: |settings_content| {
7611                        language_settings_field(settings_content, |language| {
7612                            language
7613                                .indent_guides
7614                                .as_ref()
7615                                .and_then(|indent_guides| indent_guides.coloring.as_ref())
7616                        })
7617                    },
7618                    write: |settings_content, value| {
7619                        language_settings_field_mut(settings_content, value, |language, value| {
7620                            language.indent_guides.get_or_insert_default().coloring = value;
7621                        })
7622                    },
7623                }),
7624                metadata: None,
7625                files: USER | PROJECT,
7626            }),
7627            SettingsPageItem::SettingItem(SettingItem {
7628                title: "Background Coloring",
7629                description: "Determines how indent guide backgrounds are colored.",
7630                field: Box::new(SettingField {
7631                    json_path: Some("languages.$(language).indent_guides.background_coloring"),
7632                    pick: |settings_content| {
7633                        language_settings_field(settings_content, |language| {
7634                            language.indent_guides.as_ref().and_then(|indent_guides| {
7635                                indent_guides.background_coloring.as_ref()
7636                            })
7637                        })
7638                    },
7639                    write: |settings_content, value| {
7640                        language_settings_field_mut(settings_content, value, |language, value| {
7641                            language
7642                                .indent_guides
7643                                .get_or_insert_default()
7644                                .background_coloring = value;
7645                        })
7646                    },
7647                }),
7648                metadata: None,
7649                files: USER | PROJECT,
7650            }),
7651        ]
7652    }
7653
7654    fn formatting_section() -> [SettingsPageItem; 7] {
7655        [
7656            SettingsPageItem::SectionHeader("Formatting"),
7657            SettingsPageItem::SettingItem(SettingItem {
7658                title: "Format On Save",
7659                description: "Whether or not to perform a buffer format before saving.",
7660                field: Box::new(
7661                    // TODO(settings_ui): this setting should just be a bool
7662                    SettingField {
7663                        json_path: Some("languages.$(language).format_on_save"),
7664                        pick: |settings_content| {
7665                            language_settings_field(settings_content, |language| {
7666                                language.format_on_save.as_ref()
7667                            })
7668                        },
7669                        write: |settings_content, value| {
7670                            language_settings_field_mut(
7671                                settings_content,
7672                                value,
7673                                |language, value| {
7674                                    language.format_on_save = value;
7675                                },
7676                            )
7677                        },
7678                    },
7679                ),
7680                metadata: None,
7681                files: USER | PROJECT,
7682            }),
7683            SettingsPageItem::SettingItem(SettingItem {
7684                title: "Remove Trailing Whitespace On Save",
7685                description: "Whether or not to remove any trailing whitespace from lines of a buffer before saving it.",
7686                field: Box::new(SettingField {
7687                    json_path: Some("languages.$(language).remove_trailing_whitespace_on_save"),
7688                    pick: |settings_content| {
7689                        language_settings_field(settings_content, |language| {
7690                            language.remove_trailing_whitespace_on_save.as_ref()
7691                        })
7692                    },
7693                    write: |settings_content, value| {
7694                        language_settings_field_mut(settings_content, value, |language, value| {
7695                            language.remove_trailing_whitespace_on_save = value;
7696                        })
7697                    },
7698                }),
7699                metadata: None,
7700                files: USER | PROJECT,
7701            }),
7702            SettingsPageItem::SettingItem(SettingItem {
7703                title: "Ensure Final Newline On Save",
7704                description: "Whether or not to ensure there's a single newline at the end of a buffer when saving it.",
7705                field: Box::new(SettingField {
7706                    json_path: Some("languages.$(language).ensure_final_newline_on_save"),
7707                    pick: |settings_content| {
7708                        language_settings_field(settings_content, |language| {
7709                            language.ensure_final_newline_on_save.as_ref()
7710                        })
7711                    },
7712                    write: |settings_content, value| {
7713                        language_settings_field_mut(settings_content, value, |language, value| {
7714                            language.ensure_final_newline_on_save = value;
7715                        })
7716                    },
7717                }),
7718                metadata: None,
7719                files: USER | PROJECT,
7720            }),
7721            SettingsPageItem::SettingItem(SettingItem {
7722                title: "Formatter",
7723                description: "How to perform a buffer format.",
7724                field: Box::new(
7725                    SettingField {
7726                        json_path: Some("languages.$(language).formatter"),
7727                        pick: |settings_content| {
7728                            language_settings_field(settings_content, |language| {
7729                                language.formatter.as_ref()
7730                            })
7731                        },
7732                        write: |settings_content, value| {
7733                            language_settings_field_mut(
7734                                settings_content,
7735                                value,
7736                                |language, value| {
7737                                    language.formatter = value;
7738                                },
7739                            )
7740                        },
7741                    }
7742                    .unimplemented(),
7743                ),
7744                metadata: None,
7745                files: USER | PROJECT,
7746            }),
7747            SettingsPageItem::SettingItem(SettingItem {
7748                title: "Use On Type Format",
7749                description: "Whether to use additional LSP queries to format (and amend) the code after every \"trigger\" symbol input, defined by LSP server capabilities",
7750                field: Box::new(SettingField {
7751                    json_path: Some("languages.$(language).use_on_type_format"),
7752                    pick: |settings_content| {
7753                        language_settings_field(settings_content, |language| {
7754                            language.use_on_type_format.as_ref()
7755                        })
7756                    },
7757                    write: |settings_content, value| {
7758                        language_settings_field_mut(settings_content, value, |language, value| {
7759                            language.use_on_type_format = value;
7760                        })
7761                    },
7762                }),
7763                metadata: None,
7764                files: USER | PROJECT,
7765            }),
7766            SettingsPageItem::SettingItem(SettingItem {
7767                title: "Code Actions On Format",
7768                description: "Additional code actions to run when formatting.",
7769                field: Box::new(
7770                    SettingField {
7771                        json_path: Some("languages.$(language).code_actions_on_format"),
7772                        pick: |settings_content| {
7773                            language_settings_field(settings_content, |language| {
7774                                language.code_actions_on_format.as_ref()
7775                            })
7776                        },
7777                        write: |settings_content, value| {
7778                            language_settings_field_mut(
7779                                settings_content,
7780                                value,
7781                                |language, value| {
7782                                    language.code_actions_on_format = value;
7783                                },
7784                            )
7785                        },
7786                    }
7787                    .unimplemented(),
7788                ),
7789                metadata: None,
7790                files: USER | PROJECT,
7791            }),
7792        ]
7793    }
7794
7795    fn autoclose_section() -> [SettingsPageItem; 5] {
7796        [
7797            SettingsPageItem::SectionHeader("Autoclose"),
7798            SettingsPageItem::SettingItem(SettingItem {
7799                title: "Use Autoclose",
7800                description: "Whether to automatically type closing characters for you. For example, when you type '(', Zed will automatically add a closing ')' at the correct position.",
7801                field: Box::new(SettingField {
7802                    json_path: Some("languages.$(language).use_autoclose"),
7803                    pick: |settings_content| {
7804                        language_settings_field(settings_content, |language| {
7805                            language.use_autoclose.as_ref()
7806                        })
7807                    },
7808                    write: |settings_content, value| {
7809                        language_settings_field_mut(settings_content, value, |language, value| {
7810                            language.use_autoclose = value;
7811                        })
7812                    },
7813                }),
7814                metadata: None,
7815                files: USER | PROJECT,
7816            }),
7817            SettingsPageItem::SettingItem(SettingItem {
7818                title: "Use Auto Surround",
7819                description: "Whether to automatically surround text with characters for you. For example, when you select text and type '(', Zed will automatically surround text with ().",
7820                field: Box::new(SettingField {
7821                    json_path: Some("languages.$(language).use_auto_surround"),
7822                    pick: |settings_content| {
7823                        language_settings_field(settings_content, |language| {
7824                            language.use_auto_surround.as_ref()
7825                        })
7826                    },
7827                    write: |settings_content, value| {
7828                        language_settings_field_mut(settings_content, value, |language, value| {
7829                            language.use_auto_surround = value;
7830                        })
7831                    },
7832                }),
7833                metadata: None,
7834                files: USER | PROJECT,
7835            }),
7836            SettingsPageItem::SettingItem(SettingItem {
7837                title: "Always Treat Brackets As Autoclosed",
7838                description: "Controls whether the closing characters are always skipped over and auto-removed no matter how they were inserted.",
7839                field: Box::new(SettingField {
7840                    json_path: Some("languages.$(language).always_treat_brackets_as_autoclosed"),
7841                    pick: |settings_content| {
7842                        language_settings_field(settings_content, |language| {
7843                            language.always_treat_brackets_as_autoclosed.as_ref()
7844                        })
7845                    },
7846                    write: |settings_content, value| {
7847                        language_settings_field_mut(settings_content, value, |language, value| {
7848                            language.always_treat_brackets_as_autoclosed = value;
7849                        })
7850                    },
7851                }),
7852                metadata: None,
7853                files: USER | PROJECT,
7854            }),
7855            SettingsPageItem::SettingItem(SettingItem {
7856                title: "JSX Tag Auto Close",
7857                description: "Whether to automatically close JSX tags.",
7858                field: Box::new(SettingField {
7859                    json_path: Some("languages.$(language).jsx_tag_auto_close"),
7860                    // TODO(settings_ui): this setting should just be a bool
7861                    pick: |settings_content| {
7862                        language_settings_field(settings_content, |language| {
7863                            language.jsx_tag_auto_close.as_ref()?.enabled.as_ref()
7864                        })
7865                    },
7866                    write: |settings_content, value| {
7867                        language_settings_field_mut(settings_content, value, |language, value| {
7868                            language.jsx_tag_auto_close.get_or_insert_default().enabled = value;
7869                        })
7870                    },
7871                }),
7872                metadata: None,
7873                files: USER | PROJECT,
7874            }),
7875        ]
7876    }
7877
7878    fn whitespace_section() -> [SettingsPageItem; 4] {
7879        [
7880            SettingsPageItem::SectionHeader("Whitespace"),
7881            SettingsPageItem::SettingItem(SettingItem {
7882                title: "Show Whitespaces",
7883                description: "Whether to show tabs and spaces in the editor.",
7884                field: Box::new(SettingField {
7885                    json_path: Some("languages.$(language).show_whitespaces"),
7886                    pick: |settings_content| {
7887                        language_settings_field(settings_content, |language| {
7888                            language.show_whitespaces.as_ref()
7889                        })
7890                    },
7891                    write: |settings_content, value| {
7892                        language_settings_field_mut(settings_content, value, |language, value| {
7893                            language.show_whitespaces = value;
7894                        })
7895                    },
7896                }),
7897                metadata: None,
7898                files: USER | PROJECT,
7899            }),
7900            SettingsPageItem::SettingItem(SettingItem {
7901                title: "Space Whitespace Indicator",
7902                description: "Visible character used to render space characters when show_whitespaces is enabled (default: \"\")",
7903                field: Box::new(
7904                    SettingField {
7905                        json_path: Some("languages.$(language).whitespace_map.space"),
7906                        pick: |settings_content| {
7907                            language_settings_field(settings_content, |language| {
7908                                language.whitespace_map.as_ref()?.space.as_ref()
7909                            })
7910                        },
7911                        write: |settings_content, value| {
7912                            language_settings_field_mut(
7913                                settings_content,
7914                                value,
7915                                |language, value| {
7916                                    language.whitespace_map.get_or_insert_default().space = value;
7917                                },
7918                            )
7919                        },
7920                    }
7921                    .unimplemented(),
7922                ),
7923                metadata: None,
7924                files: USER | PROJECT,
7925            }),
7926            SettingsPageItem::SettingItem(SettingItem {
7927                title: "Tab Whitespace Indicator",
7928                description: "Visible character used to render tab characters when show_whitespaces is enabled (default: \"\")",
7929                field: Box::new(
7930                    SettingField {
7931                        json_path: Some("languages.$(language).whitespace_map.tab"),
7932                        pick: |settings_content| {
7933                            language_settings_field(settings_content, |language| {
7934                                language.whitespace_map.as_ref()?.tab.as_ref()
7935                            })
7936                        },
7937                        write: |settings_content, value| {
7938                            language_settings_field_mut(
7939                                settings_content,
7940                                value,
7941                                |language, value| {
7942                                    language.whitespace_map.get_or_insert_default().tab = value;
7943                                },
7944                            )
7945                        },
7946                    }
7947                    .unimplemented(),
7948                ),
7949                metadata: None,
7950                files: USER | PROJECT,
7951            }),
7952        ]
7953    }
7954
7955    fn completions_section() -> [SettingsPageItem; 7] {
7956        [
7957            SettingsPageItem::SectionHeader("Completions"),
7958            SettingsPageItem::SettingItem(SettingItem {
7959                title: "Show Completions On Input",
7960                description: "Whether to pop the completions menu while typing in an editor without explicitly requesting it.",
7961                field: Box::new(SettingField {
7962                    json_path: Some("languages.$(language).show_completions_on_input"),
7963                    pick: |settings_content| {
7964                        language_settings_field(settings_content, |language| {
7965                            language.show_completions_on_input.as_ref()
7966                        })
7967                    },
7968                    write: |settings_content, value| {
7969                        language_settings_field_mut(settings_content, value, |language, value| {
7970                            language.show_completions_on_input = value;
7971                        })
7972                    },
7973                }),
7974                metadata: None,
7975                files: USER | PROJECT,
7976            }),
7977            SettingsPageItem::SettingItem(SettingItem {
7978                title: "Show Completion Documentation",
7979                description: "Whether to display inline and alongside documentation for items in the completions menu.",
7980                field: Box::new(SettingField {
7981                    json_path: Some("languages.$(language).show_completion_documentation"),
7982                    pick: |settings_content| {
7983                        language_settings_field(settings_content, |language| {
7984                            language.show_completion_documentation.as_ref()
7985                        })
7986                    },
7987                    write: |settings_content, value| {
7988                        language_settings_field_mut(settings_content, value, |language, value| {
7989                            language.show_completion_documentation = value;
7990                        })
7991                    },
7992                }),
7993                metadata: None,
7994                files: USER | PROJECT,
7995            }),
7996            SettingsPageItem::SettingItem(SettingItem {
7997                title: "Words",
7998                description: "Controls how words are completed.",
7999                field: Box::new(SettingField {
8000                    json_path: Some("languages.$(language).completions.words"),
8001                    pick: |settings_content| {
8002                        language_settings_field(settings_content, |language| {
8003                            language.completions.as_ref()?.words.as_ref()
8004                        })
8005                    },
8006                    write: |settings_content, value| {
8007                        language_settings_field_mut(settings_content, value, |language, value| {
8008                            language.completions.get_or_insert_default().words = value;
8009                        })
8010                    },
8011                }),
8012                metadata: None,
8013                files: USER | PROJECT,
8014            }),
8015            SettingsPageItem::SettingItem(SettingItem {
8016                title: "Words Min Length",
8017                description: "How many characters has to be in the completions query to automatically show the words-based completions.",
8018                field: Box::new(SettingField {
8019                    json_path: Some("languages.$(language).completions.words_min_length"),
8020                    pick: |settings_content| {
8021                        language_settings_field(settings_content, |language| {
8022                            language.completions.as_ref()?.words_min_length.as_ref()
8023                        })
8024                    },
8025                    write: |settings_content, value| {
8026                        language_settings_field_mut(settings_content, value, |language, value| {
8027                            language
8028                                .completions
8029                                .get_or_insert_default()
8030                                .words_min_length = value;
8031                        })
8032                    },
8033                }),
8034                metadata: None,
8035                files: USER | PROJECT,
8036            }),
8037            SettingsPageItem::SettingItem(SettingItem {
8038                title: "Completion Menu Scrollbar",
8039                description: "When to show the scrollbar in the completion menu.",
8040                field: Box::new(SettingField {
8041                    json_path: Some("editor.completion_menu_scrollbar"),
8042                    pick: |settings_content| {
8043                        settings_content.editor.completion_menu_scrollbar.as_ref()
8044                    },
8045                    write: |settings_content, value| {
8046                        settings_content.editor.completion_menu_scrollbar = value;
8047                    },
8048                }),
8049                metadata: None,
8050                files: USER,
8051            }),
8052            SettingsPageItem::SettingItem(SettingItem {
8053                title: "Completion Detail Alignment",
8054                description: "Whether to align detail text in code completions context menus left or right.",
8055                field: Box::new(SettingField {
8056                    json_path: Some("editor.completion_detail_alignment"),
8057                    pick: |settings_content| {
8058                        settings_content.editor.completion_detail_alignment.as_ref()
8059                    },
8060                    write: |settings_content, value| {
8061                        settings_content.editor.completion_detail_alignment = value;
8062                    },
8063                }),
8064                metadata: None,
8065                files: USER,
8066            }),
8067        ]
8068    }
8069
8070    fn inlay_hints_section() -> [SettingsPageItem; 10] {
8071        [
8072            SettingsPageItem::SectionHeader("Inlay Hints"),
8073            SettingsPageItem::SettingItem(SettingItem {
8074                title: "Enabled",
8075                description: "Global switch to toggle hints on and off.",
8076                field: Box::new(SettingField {
8077                    json_path: Some("languages.$(language).inlay_hints.enabled"),
8078                    pick: |settings_content| {
8079                        language_settings_field(settings_content, |language| {
8080                            language.inlay_hints.as_ref()?.enabled.as_ref()
8081                        })
8082                    },
8083                    write: |settings_content, value| {
8084                        language_settings_field_mut(settings_content, value, |language, value| {
8085                            language.inlay_hints.get_or_insert_default().enabled = value;
8086                        })
8087                    },
8088                }),
8089                metadata: None,
8090                files: USER | PROJECT,
8091            }),
8092            SettingsPageItem::SettingItem(SettingItem {
8093                title: "Show Value Hints",
8094                description: "Global switch to toggle inline values on and off when debugging.",
8095                field: Box::new(SettingField {
8096                    json_path: Some("languages.$(language).inlay_hints.show_value_hints"),
8097                    pick: |settings_content| {
8098                        language_settings_field(settings_content, |language| {
8099                            language.inlay_hints.as_ref()?.show_value_hints.as_ref()
8100                        })
8101                    },
8102                    write: |settings_content, value| {
8103                        language_settings_field_mut(settings_content, value, |language, value| {
8104                            language
8105                                .inlay_hints
8106                                .get_or_insert_default()
8107                                .show_value_hints = value;
8108                        })
8109                    },
8110                }),
8111                metadata: None,
8112                files: USER | PROJECT,
8113            }),
8114            SettingsPageItem::SettingItem(SettingItem {
8115                title: "Show Type Hints",
8116                description: "Whether type hints should be shown.",
8117                field: Box::new(SettingField {
8118                    json_path: Some("languages.$(language).inlay_hints.show_type_hints"),
8119                    pick: |settings_content| {
8120                        language_settings_field(settings_content, |language| {
8121                            language.inlay_hints.as_ref()?.show_type_hints.as_ref()
8122                        })
8123                    },
8124                    write: |settings_content, value| {
8125                        language_settings_field_mut(settings_content, value, |language, value| {
8126                            language.inlay_hints.get_or_insert_default().show_type_hints = value;
8127                        })
8128                    },
8129                }),
8130                metadata: None,
8131                files: USER | PROJECT,
8132            }),
8133            SettingsPageItem::SettingItem(SettingItem {
8134                title: "Show Parameter Hints",
8135                description: "Whether parameter hints should be shown.",
8136                field: Box::new(SettingField {
8137                    json_path: Some("languages.$(language).inlay_hints.show_parameter_hints"),
8138                    pick: |settings_content| {
8139                        language_settings_field(settings_content, |language| {
8140                            language.inlay_hints.as_ref()?.show_parameter_hints.as_ref()
8141                        })
8142                    },
8143                    write: |settings_content, value| {
8144                        language_settings_field_mut(settings_content, value, |language, value| {
8145                            language
8146                                .inlay_hints
8147                                .get_or_insert_default()
8148                                .show_parameter_hints = value;
8149                        })
8150                    },
8151                }),
8152                metadata: None,
8153                files: USER | PROJECT,
8154            }),
8155            SettingsPageItem::SettingItem(SettingItem {
8156                title: "Show Other Hints",
8157                description: "Whether other hints should be shown.",
8158                field: Box::new(SettingField {
8159                    json_path: Some("languages.$(language).inlay_hints.show_other_hints"),
8160                    pick: |settings_content| {
8161                        language_settings_field(settings_content, |language| {
8162                            language.inlay_hints.as_ref()?.show_other_hints.as_ref()
8163                        })
8164                    },
8165                    write: |settings_content, value| {
8166                        language_settings_field_mut(settings_content, value, |language, value| {
8167                            language
8168                                .inlay_hints
8169                                .get_or_insert_default()
8170                                .show_other_hints = value;
8171                        })
8172                    },
8173                }),
8174                metadata: None,
8175                files: USER | PROJECT,
8176            }),
8177            SettingsPageItem::SettingItem(SettingItem {
8178                title: "Show Background",
8179                description: "Show a background for inlay hints.",
8180                field: Box::new(SettingField {
8181                    json_path: Some("languages.$(language).inlay_hints.show_background"),
8182                    pick: |settings_content| {
8183                        language_settings_field(settings_content, |language| {
8184                            language.inlay_hints.as_ref()?.show_background.as_ref()
8185                        })
8186                    },
8187                    write: |settings_content, value| {
8188                        language_settings_field_mut(settings_content, value, |language, value| {
8189                            language.inlay_hints.get_or_insert_default().show_background = value;
8190                        })
8191                    },
8192                }),
8193                metadata: None,
8194                files: USER | PROJECT,
8195            }),
8196            SettingsPageItem::SettingItem(SettingItem {
8197                title: "Edit Debounce Ms",
8198                description: "Whether or not to debounce inlay hints updates after buffer edits (set to 0 to disable debouncing).",
8199                field: Box::new(SettingField {
8200                    json_path: Some("languages.$(language).inlay_hints.edit_debounce_ms"),
8201                    pick: |settings_content| {
8202                        language_settings_field(settings_content, |language| {
8203                            language.inlay_hints.as_ref()?.edit_debounce_ms.as_ref()
8204                        })
8205                    },
8206                    write: |settings_content, value| {
8207                        language_settings_field_mut(settings_content, value, |language, value| {
8208                            language
8209                                .inlay_hints
8210                                .get_or_insert_default()
8211                                .edit_debounce_ms = value;
8212                        })
8213                    },
8214                }),
8215                metadata: None,
8216                files: USER | PROJECT,
8217            }),
8218            SettingsPageItem::SettingItem(SettingItem {
8219                title: "Scroll Debounce Ms",
8220                description: "Whether or not to debounce inlay hints updates after buffer scrolls (set to 0 to disable debouncing).",
8221                field: Box::new(SettingField {
8222                    json_path: Some("languages.$(language).inlay_hints.scroll_debounce_ms"),
8223                    pick: |settings_content| {
8224                        language_settings_field(settings_content, |language| {
8225                            language.inlay_hints.as_ref()?.scroll_debounce_ms.as_ref()
8226                        })
8227                    },
8228                    write: |settings_content, value| {
8229                        language_settings_field_mut(settings_content, value, |language, value| {
8230                            language
8231                                .inlay_hints
8232                                .get_or_insert_default()
8233                                .scroll_debounce_ms = value;
8234                        })
8235                    },
8236                }),
8237                metadata: None,
8238                files: USER | PROJECT,
8239            }),
8240            SettingsPageItem::SettingItem(SettingItem {
8241                title: "Toggle On Modifiers Press",
8242                description: "Toggles inlay hints (hides or shows) when the user presses the modifiers specified.",
8243                field: Box::new(
8244                    SettingField {
8245                        json_path: Some(
8246                            "languages.$(language).inlay_hints.toggle_on_modifiers_press",
8247                        ),
8248                        pick: |settings_content| {
8249                            language_settings_field(settings_content, |language| {
8250                                language
8251                                    .inlay_hints
8252                                    .as_ref()?
8253                                    .toggle_on_modifiers_press
8254                                    .as_ref()
8255                            })
8256                        },
8257                        write: |settings_content, value| {
8258                            language_settings_field_mut(
8259                                settings_content,
8260                                value,
8261                                |language, value| {
8262                                    language
8263                                        .inlay_hints
8264                                        .get_or_insert_default()
8265                                        .toggle_on_modifiers_press = value;
8266                                },
8267                            )
8268                        },
8269                    }
8270                    .unimplemented(),
8271                ),
8272                metadata: None,
8273                files: USER | PROJECT,
8274            }),
8275        ]
8276    }
8277
8278    fn tasks_section() -> [SettingsPageItem; 4] {
8279        [
8280            SettingsPageItem::SectionHeader("Tasks"),
8281            SettingsPageItem::SettingItem(SettingItem {
8282                title: "Enabled",
8283                description: "Whether tasks are enabled for this language.",
8284                field: Box::new(SettingField {
8285                    json_path: Some("languages.$(language).tasks.enabled"),
8286                    pick: |settings_content| {
8287                        language_settings_field(settings_content, |language| {
8288                            language.tasks.as_ref()?.enabled.as_ref()
8289                        })
8290                    },
8291                    write: |settings_content, value| {
8292                        language_settings_field_mut(settings_content, value, |language, value| {
8293                            language.tasks.get_or_insert_default().enabled = value;
8294                        })
8295                    },
8296                }),
8297                metadata: None,
8298                files: USER | PROJECT,
8299            }),
8300            SettingsPageItem::SettingItem(SettingItem {
8301                title: "Variables",
8302                description: "Extra task variables to set for a particular language.",
8303                field: Box::new(
8304                    SettingField {
8305                        json_path: Some("languages.$(language).tasks.variables"),
8306                        pick: |settings_content| {
8307                            language_settings_field(settings_content, |language| {
8308                                language.tasks.as_ref()?.variables.as_ref()
8309                            })
8310                        },
8311                        write: |settings_content, value| {
8312                            language_settings_field_mut(
8313                                settings_content,
8314                                value,
8315                                |language, value| {
8316                                    language.tasks.get_or_insert_default().variables = value;
8317                                },
8318                            )
8319                        },
8320                    }
8321                    .unimplemented(),
8322                ),
8323                metadata: None,
8324                files: USER | PROJECT,
8325            }),
8326            SettingsPageItem::SettingItem(SettingItem {
8327                title: "Prefer LSP",
8328                description: "Use LSP tasks over Zed language extension tasks.",
8329                field: Box::new(SettingField {
8330                    json_path: Some("languages.$(language).tasks.prefer_lsp"),
8331                    pick: |settings_content| {
8332                        language_settings_field(settings_content, |language| {
8333                            language.tasks.as_ref()?.prefer_lsp.as_ref()
8334                        })
8335                    },
8336                    write: |settings_content, value| {
8337                        language_settings_field_mut(settings_content, value, |language, value| {
8338                            language.tasks.get_or_insert_default().prefer_lsp = value;
8339                        })
8340                    },
8341                }),
8342                metadata: None,
8343                files: USER | PROJECT,
8344            }),
8345        ]
8346    }
8347
8348    fn miscellaneous_section() -> [SettingsPageItem; 6] {
8349        [
8350            SettingsPageItem::SectionHeader("Miscellaneous"),
8351            SettingsPageItem::SettingItem(SettingItem {
8352                title: "Word Diff Enabled",
8353                description: "Whether to enable word diff highlighting in the editor. When enabled, changed words within modified lines are highlighted to show exactly what changed.",
8354                field: Box::new(SettingField {
8355                    json_path: Some("languages.$(language).word_diff_enabled"),
8356                    pick: |settings_content| {
8357                        language_settings_field(settings_content, |language| {
8358                            language.word_diff_enabled.as_ref()
8359                        })
8360                    },
8361                    write: |settings_content, value| {
8362                        language_settings_field_mut(settings_content, value, |language, value| {
8363                            language.word_diff_enabled = value;
8364                        })
8365                    },
8366                }),
8367                metadata: None,
8368                files: USER | PROJECT,
8369            }),
8370            SettingsPageItem::SettingItem(SettingItem {
8371                title: "Debuggers",
8372                description: "Preferred debuggers for this language.",
8373                field: Box::new(
8374                    SettingField {
8375                        json_path: Some("languages.$(language).debuggers"),
8376                        pick: |settings_content| {
8377                            language_settings_field(settings_content, |language| {
8378                                language.debuggers.as_ref()
8379                            })
8380                        },
8381                        write: |settings_content, value| {
8382                            language_settings_field_mut(
8383                                settings_content,
8384                                value,
8385                                |language, value| {
8386                                    language.debuggers = value;
8387                                },
8388                            )
8389                        },
8390                    }
8391                    .unimplemented(),
8392                ),
8393                metadata: None,
8394                files: USER | PROJECT,
8395            }),
8396            SettingsPageItem::SettingItem(SettingItem {
8397                title: "Middle Click Paste",
8398                description: "Enable middle-click paste on Linux.",
8399                field: Box::new(SettingField {
8400                    json_path: Some("languages.$(language).editor.middle_click_paste"),
8401                    pick: |settings_content| settings_content.editor.middle_click_paste.as_ref(),
8402                    write: |settings_content, value| {
8403                        settings_content.editor.middle_click_paste = value;
8404                    },
8405                }),
8406                metadata: None,
8407                files: USER,
8408            }),
8409            SettingsPageItem::SettingItem(SettingItem {
8410                title: "Extend Comment On Newline",
8411                description: "Whether to start a new line with a comment when a previous line is a comment as well.",
8412                field: Box::new(SettingField {
8413                    json_path: Some("languages.$(language).extend_comment_on_newline"),
8414                    pick: |settings_content| {
8415                        language_settings_field(settings_content, |language| {
8416                            language.extend_comment_on_newline.as_ref()
8417                        })
8418                    },
8419                    write: |settings_content, value| {
8420                        language_settings_field_mut(settings_content, value, |language, value| {
8421                            language.extend_comment_on_newline = value;
8422                        })
8423                    },
8424                }),
8425                metadata: None,
8426                files: USER | PROJECT,
8427            }),
8428            SettingsPageItem::SettingItem(SettingItem {
8429                title: "Colorize Brackets",
8430                description: "Whether to colorize brackets in the editor.",
8431                field: Box::new(SettingField {
8432                    json_path: Some("languages.$(language).colorize_brackets"),
8433                    pick: |settings_content| {
8434                        language_settings_field(settings_content, |language| {
8435                            language.colorize_brackets.as_ref()
8436                        })
8437                    },
8438                    write: |settings_content, value| {
8439                        language_settings_field_mut(settings_content, value, |language, value| {
8440                            language.colorize_brackets = value;
8441                        })
8442                    },
8443                }),
8444                metadata: None,
8445                files: USER | PROJECT,
8446            }),
8447        ]
8448    }
8449
8450    fn global_only_miscellaneous_sub_section() -> [SettingsPageItem; 3] {
8451        [
8452            SettingsPageItem::SettingItem(SettingItem {
8453                title: "Image Viewer",
8454                description: "The unit for image file sizes.",
8455                field: Box::new(SettingField {
8456                    json_path: Some("image_viewer.unit"),
8457                    pick: |settings_content| {
8458                        settings_content
8459                            .image_viewer
8460                            .as_ref()
8461                            .and_then(|image_viewer| image_viewer.unit.as_ref())
8462                    },
8463                    write: |settings_content, value| {
8464                        settings_content.image_viewer.get_or_insert_default().unit = value;
8465                    },
8466                }),
8467                metadata: None,
8468                files: USER,
8469            }),
8470            SettingsPageItem::SettingItem(SettingItem {
8471                title: "Auto Replace Emoji Shortcode",
8472                description: "Whether to automatically replace emoji shortcodes with emoji characters.",
8473                field: Box::new(SettingField {
8474                    json_path: Some("message_editor.auto_replace_emoji_shortcode"),
8475                    pick: |settings_content| {
8476                        settings_content
8477                            .message_editor
8478                            .as_ref()
8479                            .and_then(|message_editor| {
8480                                message_editor.auto_replace_emoji_shortcode.as_ref()
8481                            })
8482                    },
8483                    write: |settings_content, value| {
8484                        settings_content
8485                            .message_editor
8486                            .get_or_insert_default()
8487                            .auto_replace_emoji_shortcode = value;
8488                    },
8489                }),
8490                metadata: None,
8491                files: USER,
8492            }),
8493            SettingsPageItem::SettingItem(SettingItem {
8494                title: "Drop Size Target",
8495                description: "Relative size of the drop target in the editor that will open dropped file as a split pane.",
8496                field: Box::new(SettingField {
8497                    json_path: Some("drop_target_size"),
8498                    pick: |settings_content| settings_content.workspace.drop_target_size.as_ref(),
8499                    write: |settings_content, value| {
8500                        settings_content.workspace.drop_target_size = value;
8501                    },
8502                }),
8503                metadata: None,
8504                files: USER,
8505            }),
8506        ]
8507    }
8508
8509    let is_global = active_language().is_none();
8510
8511    let lsp_document_colors_item = [SettingsPageItem::SettingItem(SettingItem {
8512        title: "LSP Document Colors",
8513        description: "How to render LSP color previews in the editor.",
8514        field: Box::new(SettingField {
8515            json_path: Some("lsp_document_colors"),
8516            pick: |settings_content| settings_content.editor.lsp_document_colors.as_ref(),
8517            write: |settings_content, value| {
8518                settings_content.editor.lsp_document_colors = value;
8519            },
8520        }),
8521        metadata: None,
8522        files: USER,
8523    })];
8524
8525    if is_global {
8526        concat_sections!(
8527            indentation_section(),
8528            wrapping_section(),
8529            indent_guides_section(),
8530            formatting_section(),
8531            autoclose_section(),
8532            whitespace_section(),
8533            completions_section(),
8534            inlay_hints_section(),
8535            lsp_document_colors_item,
8536            tasks_section(),
8537            miscellaneous_section(),
8538            global_only_miscellaneous_sub_section(),
8539        )
8540    } else {
8541        concat_sections!(
8542            indentation_section(),
8543            wrapping_section(),
8544            indent_guides_section(),
8545            formatting_section(),
8546            autoclose_section(),
8547            whitespace_section(),
8548            completions_section(),
8549            inlay_hints_section(),
8550            tasks_section(),
8551            miscellaneous_section(),
8552        )
8553    }
8554}
8555
8556/// LanguageSettings items that should be included in the "Languages & Tools" page
8557/// not the "Editor" page
8558fn non_editor_language_settings_data() -> Box<[SettingsPageItem]> {
8559    fn lsp_section() -> [SettingsPageItem; 8] {
8560        [
8561            SettingsPageItem::SectionHeader("LSP"),
8562            SettingsPageItem::SettingItem(SettingItem {
8563                title: "Enable Language Server",
8564                description: "Whether to use language servers to provide code intelligence.",
8565                field: Box::new(SettingField {
8566                    json_path: Some("languages.$(language).enable_language_server"),
8567                    pick: |settings_content| {
8568                        language_settings_field(settings_content, |language| {
8569                            language.enable_language_server.as_ref()
8570                        })
8571                    },
8572                    write: |settings_content, value| {
8573                        language_settings_field_mut(settings_content, value, |language, value| {
8574                            language.enable_language_server = value;
8575                        })
8576                    },
8577                }),
8578                metadata: None,
8579                files: USER | PROJECT,
8580            }),
8581            SettingsPageItem::SettingItem(SettingItem {
8582                title: "Language Servers",
8583                description: "The list of language servers to use (or disable) for this language.",
8584                field: Box::new(
8585                    SettingField {
8586                        json_path: Some("languages.$(language).language_servers"),
8587                        pick: |settings_content| {
8588                            language_settings_field(settings_content, |language| {
8589                                language.language_servers.as_ref()
8590                            })
8591                        },
8592                        write: |settings_content, value| {
8593                            language_settings_field_mut(
8594                                settings_content,
8595                                value,
8596                                |language, value| {
8597                                    language.language_servers = value;
8598                                },
8599                            )
8600                        },
8601                    }
8602                    .unimplemented(),
8603                ),
8604                metadata: None,
8605                files: USER | PROJECT,
8606            }),
8607            SettingsPageItem::SettingItem(SettingItem {
8608                title: "Linked Edits",
8609                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.",
8610                field: Box::new(SettingField {
8611                    json_path: Some("languages.$(language).linked_edits"),
8612                    pick: |settings_content| {
8613                        language_settings_field(settings_content, |language| {
8614                            language.linked_edits.as_ref()
8615                        })
8616                    },
8617                    write: |settings_content, value| {
8618                        language_settings_field_mut(settings_content, value, |language, value| {
8619                            language.linked_edits = value;
8620                        })
8621                    },
8622                }),
8623                metadata: None,
8624                files: USER | PROJECT,
8625            }),
8626            SettingsPageItem::SettingItem(SettingItem {
8627                title: "Go To Definition Fallback",
8628                description: "Whether to follow-up empty Go to definition responses from the language server.",
8629                field: Box::new(SettingField {
8630                    json_path: Some("go_to_definition_fallback"),
8631                    pick: |settings_content| {
8632                        settings_content.editor.go_to_definition_fallback.as_ref()
8633                    },
8634                    write: |settings_content, value| {
8635                        settings_content.editor.go_to_definition_fallback = value;
8636                    },
8637                }),
8638                metadata: None,
8639                files: USER,
8640            }),
8641            SettingsPageItem::SettingItem(SettingItem {
8642                title: "Semantic Tokens",
8643                description: {
8644                    static DESCRIPTION: OnceLock<&'static str> = OnceLock::new();
8645                    DESCRIPTION.get_or_init(|| {
8646                        SemanticTokens::VARIANTS
8647                            .iter()
8648                            .filter_map(|v| {
8649                                v.get_documentation().map(|doc| format!("{v:?}: {doc}"))
8650                            })
8651                            .join("\n")
8652                            .leak()
8653                    })
8654                },
8655                field: Box::new(SettingField {
8656                    json_path: Some("languages.$(language).semantic_tokens"),
8657                    pick: |settings_content| {
8658                        settings_content
8659                            .project
8660                            .all_languages
8661                            .defaults
8662                            .semantic_tokens
8663                            .as_ref()
8664                    },
8665                    write: |settings_content, value| {
8666                        settings_content
8667                            .project
8668                            .all_languages
8669                            .defaults
8670                            .semantic_tokens = value;
8671                    },
8672                }),
8673                metadata: None,
8674                files: USER | PROJECT,
8675            }),
8676            SettingsPageItem::SettingItem(SettingItem {
8677                title: "LSP Folding Ranges",
8678                description: "When enabled, use folding ranges from the language server instead of indent-based folding.",
8679                field: Box::new(SettingField {
8680                    json_path: Some("languages.$(language).document_folding_ranges"),
8681                    pick: |settings_content| {
8682                        language_settings_field(settings_content, |language| {
8683                            language.document_folding_ranges.as_ref()
8684                        })
8685                    },
8686                    write: |settings_content, value| {
8687                        language_settings_field_mut(settings_content, value, |language, value| {
8688                            language.document_folding_ranges = value;
8689                        })
8690                    },
8691                }),
8692                metadata: None,
8693                files: USER | PROJECT,
8694            }),
8695            SettingsPageItem::SettingItem(SettingItem {
8696                title: "LSP Document Symbols",
8697                description: "When enabled, use the language server's document symbols for outlines and breadcrumbs instead of tree-sitter.",
8698                field: Box::new(SettingField {
8699                    json_path: Some("languages.$(language).document_symbols"),
8700                    pick: |settings_content| {
8701                        language_settings_field(settings_content, |language| {
8702                            language.document_symbols.as_ref()
8703                        })
8704                    },
8705                    write: |settings_content, value| {
8706                        language_settings_field_mut(settings_content, value, |language, value| {
8707                            language.document_symbols = value;
8708                        })
8709                    },
8710                }),
8711                metadata: None,
8712                files: USER | PROJECT,
8713            }),
8714        ]
8715    }
8716
8717    fn lsp_completions_section() -> [SettingsPageItem; 4] {
8718        [
8719            SettingsPageItem::SectionHeader("LSP Completions"),
8720            SettingsPageItem::SettingItem(SettingItem {
8721                title: "Enabled",
8722                description: "Whether to fetch LSP completions or not.",
8723                field: Box::new(SettingField {
8724                    json_path: Some("languages.$(language).completions.lsp"),
8725                    pick: |settings_content| {
8726                        language_settings_field(settings_content, |language| {
8727                            language.completions.as_ref()?.lsp.as_ref()
8728                        })
8729                    },
8730                    write: |settings_content, value| {
8731                        language_settings_field_mut(settings_content, value, |language, value| {
8732                            language.completions.get_or_insert_default().lsp = value;
8733                        })
8734                    },
8735                }),
8736                metadata: None,
8737                files: USER | PROJECT,
8738            }),
8739            SettingsPageItem::SettingItem(SettingItem {
8740                title: "Fetch Timeout (milliseconds)",
8741                description: "When fetching LSP completions, determines how long to wait for a response of a particular server (set to 0 to wait indefinitely).",
8742                field: Box::new(SettingField {
8743                    json_path: Some("languages.$(language).completions.lsp_fetch_timeout_ms"),
8744                    pick: |settings_content| {
8745                        language_settings_field(settings_content, |language| {
8746                            language.completions.as_ref()?.lsp_fetch_timeout_ms.as_ref()
8747                        })
8748                    },
8749                    write: |settings_content, value| {
8750                        language_settings_field_mut(settings_content, value, |language, value| {
8751                            language
8752                                .completions
8753                                .get_or_insert_default()
8754                                .lsp_fetch_timeout_ms = value;
8755                        })
8756                    },
8757                }),
8758                metadata: None,
8759                files: USER | PROJECT,
8760            }),
8761            SettingsPageItem::SettingItem(SettingItem {
8762                title: "Insert Mode",
8763                description: "Controls how LSP completions are inserted.",
8764                field: Box::new(SettingField {
8765                    json_path: Some("languages.$(language).completions.lsp_insert_mode"),
8766                    pick: |settings_content| {
8767                        language_settings_field(settings_content, |language| {
8768                            language.completions.as_ref()?.lsp_insert_mode.as_ref()
8769                        })
8770                    },
8771                    write: |settings_content, value| {
8772                        language_settings_field_mut(settings_content, value, |language, value| {
8773                            language.completions.get_or_insert_default().lsp_insert_mode = value;
8774                        })
8775                    },
8776                }),
8777                metadata: None,
8778                files: USER | PROJECT,
8779            }),
8780        ]
8781    }
8782
8783    fn debugger_section() -> [SettingsPageItem; 2] {
8784        [
8785            SettingsPageItem::SectionHeader("Debuggers"),
8786            SettingsPageItem::SettingItem(SettingItem {
8787                title: "Debuggers",
8788                description: "Preferred debuggers for this language.",
8789                field: Box::new(
8790                    SettingField {
8791                        json_path: Some("languages.$(language).debuggers"),
8792                        pick: |settings_content| {
8793                            language_settings_field(settings_content, |language| {
8794                                language.debuggers.as_ref()
8795                            })
8796                        },
8797                        write: |settings_content, value| {
8798                            language_settings_field_mut(
8799                                settings_content,
8800                                value,
8801                                |language, value| {
8802                                    language.debuggers = value;
8803                                },
8804                            )
8805                        },
8806                    }
8807                    .unimplemented(),
8808                ),
8809                metadata: None,
8810                files: USER | PROJECT,
8811            }),
8812        ]
8813    }
8814
8815    fn prettier_section() -> [SettingsPageItem; 5] {
8816        [
8817            SettingsPageItem::SectionHeader("Prettier"),
8818            SettingsPageItem::SettingItem(SettingItem {
8819                title: "Allowed",
8820                description: "Enables or disables formatting with Prettier for a given language.",
8821                field: Box::new(SettingField {
8822                    json_path: Some("languages.$(language).prettier.allowed"),
8823                    pick: |settings_content| {
8824                        language_settings_field(settings_content, |language| {
8825                            language.prettier.as_ref()?.allowed.as_ref()
8826                        })
8827                    },
8828                    write: |settings_content, value| {
8829                        language_settings_field_mut(settings_content, value, |language, value| {
8830                            language.prettier.get_or_insert_default().allowed = value;
8831                        })
8832                    },
8833                }),
8834                metadata: None,
8835                files: USER | PROJECT,
8836            }),
8837            SettingsPageItem::SettingItem(SettingItem {
8838                title: "Parser",
8839                description: "Forces Prettier integration to use a specific parser name when formatting files with the language.",
8840                field: Box::new(SettingField {
8841                    json_path: Some("languages.$(language).prettier.parser"),
8842                    pick: |settings_content| {
8843                        language_settings_field(settings_content, |language| {
8844                            language.prettier.as_ref()?.parser.as_ref()
8845                        })
8846                    },
8847                    write: |settings_content, value| {
8848                        language_settings_field_mut(settings_content, value, |language, value| {
8849                            language.prettier.get_or_insert_default().parser = value;
8850                        })
8851                    },
8852                }),
8853                metadata: None,
8854                files: USER | PROJECT,
8855            }),
8856            SettingsPageItem::SettingItem(SettingItem {
8857                title: "Plugins",
8858                description: "Forces Prettier integration to use specific plugins when formatting files with the language.",
8859                field: Box::new(
8860                    SettingField {
8861                        json_path: Some("languages.$(language).prettier.plugins"),
8862                        pick: |settings_content| {
8863                            language_settings_field(settings_content, |language| {
8864                                language.prettier.as_ref()?.plugins.as_ref()
8865                            })
8866                        },
8867                        write: |settings_content, value| {
8868                            language_settings_field_mut(
8869                                settings_content,
8870                                value,
8871                                |language, value| {
8872                                    language.prettier.get_or_insert_default().plugins = value;
8873                                },
8874                            )
8875                        },
8876                    }
8877                    .unimplemented(),
8878                ),
8879                metadata: None,
8880                files: USER | PROJECT,
8881            }),
8882            SettingsPageItem::SettingItem(SettingItem {
8883                title: "Options",
8884                description: "Default Prettier options, in the format as in package.json section for Prettier.",
8885                field: Box::new(
8886                    SettingField {
8887                        json_path: Some("languages.$(language).prettier.options"),
8888                        pick: |settings_content| {
8889                            language_settings_field(settings_content, |language| {
8890                                language.prettier.as_ref()?.options.as_ref()
8891                            })
8892                        },
8893                        write: |settings_content, value| {
8894                            language_settings_field_mut(
8895                                settings_content,
8896                                value,
8897                                |language, value| {
8898                                    language.prettier.get_or_insert_default().options = value;
8899                                },
8900                            )
8901                        },
8902                    }
8903                    .unimplemented(),
8904                ),
8905                metadata: None,
8906                files: USER | PROJECT,
8907            }),
8908        ]
8909    }
8910
8911    concat_sections!(
8912        lsp_section(),
8913        lsp_completions_section(),
8914        debugger_section(),
8915        prettier_section(),
8916    )
8917}
8918
8919fn edit_prediction_language_settings_section() -> [SettingsPageItem; 4] {
8920    [
8921        SettingsPageItem::SectionHeader("Edit Predictions"),
8922        SettingsPageItem::SubPageLink(SubPageLink {
8923            title: "Configure Providers".into(),
8924            r#type: Default::default(),
8925            json_path: Some("edit_predictions.providers"),
8926            description: Some("Set up different edit prediction providers in complement to Zed's built-in Zeta model.".into()),
8927            in_json: false,
8928            files: USER,
8929            render: render_edit_prediction_setup_page
8930        }),
8931        SettingsPageItem::SettingItem(SettingItem {
8932            title: "Show Edit Predictions",
8933            description: "Controls whether edit predictions are shown immediately or manually.",
8934            field: Box::new(SettingField {
8935                json_path: Some("languages.$(language).show_edit_predictions"),
8936                pick: |settings_content| {
8937                    language_settings_field(settings_content, |language| {
8938                        language.show_edit_predictions.as_ref()
8939                    })
8940                },
8941                write: |settings_content, value| {
8942                    language_settings_field_mut(settings_content, value, |language, value| {
8943                        language.show_edit_predictions = value;
8944                    })
8945                },
8946            }),
8947            metadata: None,
8948            files: USER | PROJECT,
8949        }),
8950        SettingsPageItem::SettingItem(SettingItem {
8951            title: "Disable in Language Scopes",
8952            description: "Controls whether edit predictions are shown in the given language scopes.",
8953            field: Box::new(
8954                SettingField {
8955                    json_path: Some("languages.$(language).edit_predictions_disabled_in"),
8956                    pick: |settings_content| {
8957                        language_settings_field(settings_content, |language| {
8958                            language.edit_predictions_disabled_in.as_ref()
8959                        })
8960                    },
8961                    write: |settings_content, value| {
8962                        language_settings_field_mut(settings_content, value, |language, value| {
8963                            language.edit_predictions_disabled_in = value;
8964                        })
8965                    },
8966                }
8967                .unimplemented(),
8968            ),
8969            metadata: None,
8970            files: USER | PROJECT,
8971        }),
8972    ]
8973}
8974
8975fn show_scrollbar_or_editor(
8976    settings_content: &SettingsContent,
8977    show: fn(&SettingsContent) -> Option<&settings::ShowScrollbar>,
8978) -> Option<&settings::ShowScrollbar> {
8979    show(settings_content).or(settings_content
8980        .editor
8981        .scrollbar
8982        .as_ref()
8983        .and_then(|scrollbar| scrollbar.show.as_ref()))
8984}
8985
8986fn dynamic_variants<T>() -> &'static [T::Discriminant]
8987where
8988    T: strum::IntoDiscriminant,
8989    T::Discriminant: strum::VariantArray,
8990{
8991    <<T as strum::IntoDiscriminant>::Discriminant as strum::VariantArray>::VARIANTS
8992}