page_data.rs

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