page_data.rs

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