page_data.rs

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