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