page_data.rs

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