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