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