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