page_data.rs

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