page_data.rs

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