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; 6] {
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            SettingsPageItem::SettingItem(SettingItem {
4227                title: "Focus Follows Mouse",
4228                description: "Whether to change focus to a pane when the mouse hovers over it.",
4229                field: Box::new(SettingField {
4230                    json_path: Some("focus_follows_mouse.enabled"),
4231                    pick: |settings_content| {
4232                        settings_content
4233                            .workspace
4234                            .focus_follows_mouse
4235                            .as_ref()
4236                            .and_then(|s| s.enabled.as_ref())
4237                    },
4238                    write: |settings_content, value| {
4239                        settings_content
4240                            .workspace
4241                            .focus_follows_mouse
4242                            .get_or_insert_default()
4243                            .enabled = value;
4244                    },
4245                }),
4246                metadata: None,
4247                files: USER,
4248            }),
4249            SettingsPageItem::SettingItem(SettingItem {
4250                title: "Focus Follows Mouse Debounce ms",
4251                description: "Amount of time to wait before changing focus.",
4252                field: Box::new(SettingField {
4253                    json_path: Some("focus_follows_mouse.debounce_ms"),
4254                    pick: |settings_content| {
4255                        settings_content
4256                            .workspace
4257                            .focus_follows_mouse
4258                            .as_ref()
4259                            .and_then(|s| s.debounce_ms.as_ref())
4260                    },
4261                    write: |settings_content, value| {
4262                        settings_content
4263                            .workspace
4264                            .focus_follows_mouse
4265                            .get_or_insert_default()
4266                            .debounce_ms = value;
4267                    },
4268                }),
4269                metadata: None,
4270                files: USER,
4271            }),
4272        ]
4273    }
4274
4275    fn window_section() -> [SettingsPageItem; 3] {
4276        [
4277            SettingsPageItem::SectionHeader("Window"),
4278            // todo(settings_ui): Should we filter by platform.as_ref()?
4279            SettingsPageItem::SettingItem(SettingItem {
4280                title: "Use System Window Tabs",
4281                description: "(macOS only) whether to allow Windows to tab together.",
4282                field: Box::new(SettingField {
4283                    json_path: Some("use_system_window_tabs"),
4284                    pick: |settings_content| {
4285                        settings_content.workspace.use_system_window_tabs.as_ref()
4286                    },
4287                    write: |settings_content, value| {
4288                        settings_content.workspace.use_system_window_tabs = value;
4289                    },
4290                }),
4291                metadata: None,
4292                files: USER,
4293            }),
4294            SettingsPageItem::SettingItem(SettingItem {
4295                title: "Window Decorations",
4296                description: "(Linux only) whether Zed or your compositor should draw window decorations.",
4297                field: Box::new(SettingField {
4298                    json_path: Some("window_decorations"),
4299                    pick: |settings_content| settings_content.workspace.window_decorations.as_ref(),
4300                    write: |settings_content, value| {
4301                        settings_content.workspace.window_decorations = value;
4302                    },
4303                }),
4304                metadata: None,
4305                files: USER,
4306            }),
4307        ]
4308    }
4309
4310    fn pane_modifiers_section() -> [SettingsPageItem; 4] {
4311        [
4312            SettingsPageItem::SectionHeader("Pane Modifiers"),
4313            SettingsPageItem::SettingItem(SettingItem {
4314                title: "Inactive Opacity",
4315                description: "Opacity of inactive panels (0.0 - 1.0).",
4316                field: Box::new(SettingField {
4317                    json_path: Some("active_pane_modifiers.inactive_opacity"),
4318                    pick: |settings_content| {
4319                        settings_content
4320                            .workspace
4321                            .active_pane_modifiers
4322                            .as_ref()?
4323                            .inactive_opacity
4324                            .as_ref()
4325                    },
4326                    write: |settings_content, value| {
4327                        settings_content
4328                            .workspace
4329                            .active_pane_modifiers
4330                            .get_or_insert_default()
4331                            .inactive_opacity = value;
4332                    },
4333                }),
4334                metadata: None,
4335                files: USER,
4336            }),
4337            SettingsPageItem::SettingItem(SettingItem {
4338                title: "Border Size",
4339                description: "Size of the border surrounding the active pane.",
4340                field: Box::new(SettingField {
4341                    json_path: Some("active_pane_modifiers.border_size"),
4342                    pick: |settings_content| {
4343                        settings_content
4344                            .workspace
4345                            .active_pane_modifiers
4346                            .as_ref()?
4347                            .border_size
4348                            .as_ref()
4349                    },
4350                    write: |settings_content, value| {
4351                        settings_content
4352                            .workspace
4353                            .active_pane_modifiers
4354                            .get_or_insert_default()
4355                            .border_size = value;
4356                    },
4357                }),
4358                metadata: None,
4359                files: USER,
4360            }),
4361            SettingsPageItem::SettingItem(SettingItem {
4362                title: "Zoomed Padding",
4363                description: "Show padding for zoomed panes.",
4364                field: Box::new(SettingField {
4365                    json_path: Some("zoomed_padding"),
4366                    pick: |settings_content| settings_content.workspace.zoomed_padding.as_ref(),
4367                    write: |settings_content, value| {
4368                        settings_content.workspace.zoomed_padding = value;
4369                    },
4370                }),
4371                metadata: None,
4372                files: USER,
4373            }),
4374        ]
4375    }
4376
4377    fn pane_split_direction_section() -> [SettingsPageItem; 3] {
4378        [
4379            SettingsPageItem::SectionHeader("Pane Split Direction"),
4380            SettingsPageItem::SettingItem(SettingItem {
4381                title: "Vertical Split Direction",
4382                description: "Direction to split vertically.",
4383                field: Box::new(SettingField {
4384                    json_path: Some("pane_split_direction_vertical"),
4385                    pick: |settings_content| {
4386                        settings_content
4387                            .workspace
4388                            .pane_split_direction_vertical
4389                            .as_ref()
4390                    },
4391                    write: |settings_content, value| {
4392                        settings_content.workspace.pane_split_direction_vertical = value;
4393                    },
4394                }),
4395                metadata: None,
4396                files: USER,
4397            }),
4398            SettingsPageItem::SettingItem(SettingItem {
4399                title: "Horizontal Split Direction",
4400                description: "Direction to split horizontally.",
4401                field: Box::new(SettingField {
4402                    json_path: Some("pane_split_direction_horizontal"),
4403                    pick: |settings_content| {
4404                        settings_content
4405                            .workspace
4406                            .pane_split_direction_horizontal
4407                            .as_ref()
4408                    },
4409                    write: |settings_content, value| {
4410                        settings_content.workspace.pane_split_direction_horizontal = value;
4411                    },
4412                }),
4413                metadata: None,
4414                files: USER,
4415            }),
4416        ]
4417    }
4418
4419    SettingsPage {
4420        title: "Window & Layout",
4421        items: concat_sections![
4422            status_bar_section(),
4423            title_bar_section(),
4424            tab_bar_section(),
4425            tab_settings_section(),
4426            preview_tabs_section(),
4427            layout_section(),
4428            window_section(),
4429            pane_modifiers_section(),
4430            pane_split_direction_section(),
4431        ],
4432    }
4433}
4434
4435fn panels_page() -> SettingsPage {
4436    fn project_panel_section() -> [SettingsPageItem; 28] {
4437        [
4438            SettingsPageItem::SectionHeader("Project Panel"),
4439            SettingsPageItem::SettingItem(SettingItem {
4440                title: "Project Panel Dock",
4441                description: "Where to dock the project panel.",
4442                field: Box::new(SettingField {
4443                    json_path: Some("project_panel.dock"),
4444                    pick: |settings_content| settings_content.project_panel.as_ref()?.dock.as_ref(),
4445                    write: |settings_content, value| {
4446                        settings_content.project_panel.get_or_insert_default().dock = value;
4447                    },
4448                }),
4449                metadata: None,
4450                files: USER,
4451            }),
4452            SettingsPageItem::SettingItem(SettingItem {
4453                title: "Project Panel Default Width",
4454                description: "Default width of the project panel in pixels.",
4455                field: Box::new(SettingField {
4456                    json_path: Some("project_panel.default_width"),
4457                    pick: |settings_content| {
4458                        settings_content
4459                            .project_panel
4460                            .as_ref()?
4461                            .default_width
4462                            .as_ref()
4463                    },
4464                    write: |settings_content, value| {
4465                        settings_content
4466                            .project_panel
4467                            .get_or_insert_default()
4468                            .default_width = value;
4469                    },
4470                }),
4471                metadata: None,
4472                files: USER,
4473            }),
4474            SettingsPageItem::SettingItem(SettingItem {
4475                title: "Hide .gitignore",
4476                description: "Whether to hide the gitignore entries in the project panel.",
4477                field: Box::new(SettingField {
4478                    json_path: Some("project_panel.hide_gitignore"),
4479                    pick: |settings_content| {
4480                        settings_content
4481                            .project_panel
4482                            .as_ref()?
4483                            .hide_gitignore
4484                            .as_ref()
4485                    },
4486                    write: |settings_content, value| {
4487                        settings_content
4488                            .project_panel
4489                            .get_or_insert_default()
4490                            .hide_gitignore = value;
4491                    },
4492                }),
4493                metadata: None,
4494                files: USER,
4495            }),
4496            SettingsPageItem::SettingItem(SettingItem {
4497                title: "Entry Spacing",
4498                description: "Spacing between worktree entries in the project panel.",
4499                field: Box::new(SettingField {
4500                    json_path: Some("project_panel.entry_spacing"),
4501                    pick: |settings_content| {
4502                        settings_content
4503                            .project_panel
4504                            .as_ref()?
4505                            .entry_spacing
4506                            .as_ref()
4507                    },
4508                    write: |settings_content, value| {
4509                        settings_content
4510                            .project_panel
4511                            .get_or_insert_default()
4512                            .entry_spacing = value;
4513                    },
4514                }),
4515                metadata: None,
4516                files: USER,
4517            }),
4518            SettingsPageItem::SettingItem(SettingItem {
4519                title: "File Icons",
4520                description: "Show file icons in the project panel.",
4521                field: Box::new(SettingField {
4522                    json_path: Some("project_panel.file_icons"),
4523                    pick: |settings_content| {
4524                        settings_content.project_panel.as_ref()?.file_icons.as_ref()
4525                    },
4526                    write: |settings_content, value| {
4527                        settings_content
4528                            .project_panel
4529                            .get_or_insert_default()
4530                            .file_icons = value;
4531                    },
4532                }),
4533                metadata: None,
4534                files: USER,
4535            }),
4536            SettingsPageItem::SettingItem(SettingItem {
4537                title: "Folder Icons",
4538                description: "Whether to show folder icons or chevrons for directories in the project panel.",
4539                field: Box::new(SettingField {
4540                    json_path: Some("project_panel.folder_icons"),
4541                    pick: |settings_content| {
4542                        settings_content
4543                            .project_panel
4544                            .as_ref()?
4545                            .folder_icons
4546                            .as_ref()
4547                    },
4548                    write: |settings_content, value| {
4549                        settings_content
4550                            .project_panel
4551                            .get_or_insert_default()
4552                            .folder_icons = value;
4553                    },
4554                }),
4555                metadata: None,
4556                files: USER,
4557            }),
4558            SettingsPageItem::SettingItem(SettingItem {
4559                title: "Git Status",
4560                description: "Show the Git status in the project panel.",
4561                field: Box::new(SettingField {
4562                    json_path: Some("project_panel.git_status"),
4563                    pick: |settings_content| {
4564                        settings_content.project_panel.as_ref()?.git_status.as_ref()
4565                    },
4566                    write: |settings_content, value| {
4567                        settings_content
4568                            .project_panel
4569                            .get_or_insert_default()
4570                            .git_status = value;
4571                    },
4572                }),
4573                metadata: None,
4574                files: USER,
4575            }),
4576            SettingsPageItem::SettingItem(SettingItem {
4577                title: "Indent Size",
4578                description: "Amount of indentation for nested items.",
4579                field: Box::new(SettingField {
4580                    json_path: Some("project_panel.indent_size"),
4581                    pick: |settings_content| {
4582                        settings_content
4583                            .project_panel
4584                            .as_ref()?
4585                            .indent_size
4586                            .as_ref()
4587                    },
4588                    write: |settings_content, value| {
4589                        settings_content
4590                            .project_panel
4591                            .get_or_insert_default()
4592                            .indent_size = value;
4593                    },
4594                }),
4595                metadata: None,
4596                files: USER,
4597            }),
4598            SettingsPageItem::SettingItem(SettingItem {
4599                title: "Auto Reveal Entries",
4600                description: "Whether to reveal entries in the project panel automatically when a corresponding project entry becomes active.",
4601                field: Box::new(SettingField {
4602                    json_path: Some("project_panel.auto_reveal_entries"),
4603                    pick: |settings_content| {
4604                        settings_content
4605                            .project_panel
4606                            .as_ref()?
4607                            .auto_reveal_entries
4608                            .as_ref()
4609                    },
4610                    write: |settings_content, value| {
4611                        settings_content
4612                            .project_panel
4613                            .get_or_insert_default()
4614                            .auto_reveal_entries = value;
4615                    },
4616                }),
4617                metadata: None,
4618                files: USER,
4619            }),
4620            SettingsPageItem::SettingItem(SettingItem {
4621                title: "Starts Open",
4622                description: "Whether the project panel should open on startup.",
4623                field: Box::new(SettingField {
4624                    json_path: Some("project_panel.starts_open"),
4625                    pick: |settings_content| {
4626                        settings_content
4627                            .project_panel
4628                            .as_ref()?
4629                            .starts_open
4630                            .as_ref()
4631                    },
4632                    write: |settings_content, value| {
4633                        settings_content
4634                            .project_panel
4635                            .get_or_insert_default()
4636                            .starts_open = value;
4637                    },
4638                }),
4639                metadata: None,
4640                files: USER,
4641            }),
4642            SettingsPageItem::SettingItem(SettingItem {
4643                title: "Auto Fold Directories",
4644                description: "Whether to fold directories automatically and show compact folders when a directory has only one subdirectory inside.",
4645                field: Box::new(SettingField {
4646                    json_path: Some("project_panel.auto_fold_dirs"),
4647                    pick: |settings_content| {
4648                        settings_content
4649                            .project_panel
4650                            .as_ref()?
4651                            .auto_fold_dirs
4652                            .as_ref()
4653                    },
4654                    write: |settings_content, value| {
4655                        settings_content
4656                            .project_panel
4657                            .get_or_insert_default()
4658                            .auto_fold_dirs = value;
4659                    },
4660                }),
4661                metadata: None,
4662                files: USER,
4663            }),
4664            SettingsPageItem::SettingItem(SettingItem {
4665                title: "Bold Folder Labels",
4666                description: "Whether to show folder names with bold text in the project panel.",
4667                field: Box::new(SettingField {
4668                    json_path: Some("project_panel.bold_folder_labels"),
4669                    pick: |settings_content| {
4670                        settings_content
4671                            .project_panel
4672                            .as_ref()?
4673                            .bold_folder_labels
4674                            .as_ref()
4675                    },
4676                    write: |settings_content, value| {
4677                        settings_content
4678                            .project_panel
4679                            .get_or_insert_default()
4680                            .bold_folder_labels = value;
4681                    },
4682                }),
4683                metadata: None,
4684                files: USER,
4685            }),
4686            SettingsPageItem::SettingItem(SettingItem {
4687                title: "Show Scrollbar",
4688                description: "Show the scrollbar in the project panel.",
4689                field: Box::new(SettingField {
4690                    json_path: Some("project_panel.scrollbar.show"),
4691                    pick: |settings_content| {
4692                        show_scrollbar_or_editor(settings_content, |settings_content| {
4693                            settings_content
4694                                .project_panel
4695                                .as_ref()?
4696                                .scrollbar
4697                                .as_ref()?
4698                                .show
4699                                .as_ref()
4700                        })
4701                    },
4702                    write: |settings_content, value| {
4703                        settings_content
4704                            .project_panel
4705                            .get_or_insert_default()
4706                            .scrollbar
4707                            .get_or_insert_default()
4708                            .show = value;
4709                    },
4710                }),
4711                metadata: None,
4712                files: USER,
4713            }),
4714            SettingsPageItem::SettingItem(SettingItem {
4715                title: "Horizontal Scroll",
4716                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.",
4717                field: Box::new(SettingField {
4718                    json_path: Some("project_panel.scrollbar.horizontal_scroll"),
4719                    pick: |settings_content| {
4720                        settings_content
4721                            .project_panel
4722                            .as_ref()?
4723                            .scrollbar
4724                            .as_ref()?
4725                            .horizontal_scroll
4726                            .as_ref()
4727                    },
4728                    write: |settings_content, value| {
4729                        settings_content
4730                            .project_panel
4731                            .get_or_insert_default()
4732                            .scrollbar
4733                            .get_or_insert_default()
4734                            .horizontal_scroll = value;
4735                    },
4736                }),
4737                metadata: None,
4738                files: USER,
4739            }),
4740            SettingsPageItem::SettingItem(SettingItem {
4741                title: "Show Diagnostics",
4742                description: "Which files containing diagnostic errors/warnings to mark in the project panel.",
4743                field: Box::new(SettingField {
4744                    json_path: Some("project_panel.show_diagnostics"),
4745                    pick: |settings_content| {
4746                        settings_content
4747                            .project_panel
4748                            .as_ref()?
4749                            .show_diagnostics
4750                            .as_ref()
4751                    },
4752                    write: |settings_content, value| {
4753                        settings_content
4754                            .project_panel
4755                            .get_or_insert_default()
4756                            .show_diagnostics = value;
4757                    },
4758                }),
4759                metadata: None,
4760                files: USER,
4761            }),
4762            SettingsPageItem::SettingItem(SettingItem {
4763                title: "Diagnostic Badges",
4764                description: "Show error and warning count badges next to file names in the project panel.",
4765                field: Box::new(SettingField {
4766                    json_path: Some("project_panel.diagnostic_badges"),
4767                    pick: |settings_content| {
4768                        settings_content
4769                            .project_panel
4770                            .as_ref()?
4771                            .diagnostic_badges
4772                            .as_ref()
4773                    },
4774                    write: |settings_content, value| {
4775                        settings_content
4776                            .project_panel
4777                            .get_or_insert_default()
4778                            .diagnostic_badges = value;
4779                    },
4780                }),
4781                metadata: None,
4782                files: USER,
4783            }),
4784            SettingsPageItem::SettingItem(SettingItem {
4785                title: "Git Status Indicator",
4786                description: "Show a git status indicator next to file names in the project panel.",
4787                field: Box::new(SettingField {
4788                    json_path: Some("project_panel.git_status_indicator"),
4789                    pick: |settings_content| {
4790                        settings_content
4791                            .project_panel
4792                            .as_ref()?
4793                            .git_status_indicator
4794                            .as_ref()
4795                    },
4796                    write: |settings_content, value| {
4797                        settings_content
4798                            .project_panel
4799                            .get_or_insert_default()
4800                            .git_status_indicator = value;
4801                    },
4802                }),
4803                metadata: None,
4804                files: USER,
4805            }),
4806            SettingsPageItem::SettingItem(SettingItem {
4807                title: "Sticky Scroll",
4808                description: "Whether to stick parent directories at top of the project panel.",
4809                field: Box::new(SettingField {
4810                    json_path: Some("project_panel.sticky_scroll"),
4811                    pick: |settings_content| {
4812                        settings_content
4813                            .project_panel
4814                            .as_ref()?
4815                            .sticky_scroll
4816                            .as_ref()
4817                    },
4818                    write: |settings_content, value| {
4819                        settings_content
4820                            .project_panel
4821                            .get_or_insert_default()
4822                            .sticky_scroll = value;
4823                    },
4824                }),
4825                metadata: None,
4826                files: USER,
4827            }),
4828            SettingsPageItem::SettingItem(SettingItem {
4829                files: USER,
4830                title: "Show Indent Guides",
4831                description: "Show indent guides in the project panel.",
4832                field: Box::new(SettingField {
4833                    json_path: Some("project_panel.indent_guides.show"),
4834                    pick: |settings_content| {
4835                        settings_content
4836                            .project_panel
4837                            .as_ref()?
4838                            .indent_guides
4839                            .as_ref()?
4840                            .show
4841                            .as_ref()
4842                    },
4843                    write: |settings_content, value| {
4844                        settings_content
4845                            .project_panel
4846                            .get_or_insert_default()
4847                            .indent_guides
4848                            .get_or_insert_default()
4849                            .show = value;
4850                    },
4851                }),
4852                metadata: None,
4853            }),
4854            SettingsPageItem::SettingItem(SettingItem {
4855                title: "Drag and Drop",
4856                description: "Whether to enable drag-and-drop operations in the project panel.",
4857                field: Box::new(SettingField {
4858                    json_path: Some("project_panel.drag_and_drop"),
4859                    pick: |settings_content| {
4860                        settings_content
4861                            .project_panel
4862                            .as_ref()?
4863                            .drag_and_drop
4864                            .as_ref()
4865                    },
4866                    write: |settings_content, value| {
4867                        settings_content
4868                            .project_panel
4869                            .get_or_insert_default()
4870                            .drag_and_drop = value;
4871                    },
4872                }),
4873                metadata: None,
4874                files: USER,
4875            }),
4876            SettingsPageItem::SettingItem(SettingItem {
4877                title: "Hide Root",
4878                description: "Whether to hide the root entry when only one folder is open in the window.",
4879                field: Box::new(SettingField {
4880                    json_path: Some("project_panel.hide_root"),
4881                    pick: |settings_content| {
4882                        settings_content.project_panel.as_ref()?.hide_root.as_ref()
4883                    },
4884                    write: |settings_content, value| {
4885                        settings_content
4886                            .project_panel
4887                            .get_or_insert_default()
4888                            .hide_root = value;
4889                    },
4890                }),
4891                metadata: None,
4892                files: USER,
4893            }),
4894            SettingsPageItem::SettingItem(SettingItem {
4895                title: "Hide Hidden",
4896                description: "Whether to hide the hidden entries in the project panel.",
4897                field: Box::new(SettingField {
4898                    json_path: Some("project_panel.hide_hidden"),
4899                    pick: |settings_content| {
4900                        settings_content
4901                            .project_panel
4902                            .as_ref()?
4903                            .hide_hidden
4904                            .as_ref()
4905                    },
4906                    write: |settings_content, value| {
4907                        settings_content
4908                            .project_panel
4909                            .get_or_insert_default()
4910                            .hide_hidden = value;
4911                    },
4912                }),
4913                metadata: None,
4914                files: USER,
4915            }),
4916            SettingsPageItem::SettingItem(SettingItem {
4917                title: "Sort Mode",
4918                description: "Sort order for entries in the project panel.",
4919                field: Box::new(SettingField {
4920                    json_path: Some("project_panel.sort_mode"),
4921                    pick: |settings_content| {
4922                        settings_content.project_panel.as_ref()?.sort_mode.as_ref()
4923                    },
4924                    write: |settings_content, value| {
4925                        settings_content
4926                            .project_panel
4927                            .get_or_insert_default()
4928                            .sort_mode = value;
4929                    },
4930                }),
4931                metadata: None,
4932                files: USER,
4933            }),
4934            SettingsPageItem::SettingItem(SettingItem {
4935                title: "Auto Open Files On Create",
4936                description: "Whether to automatically open newly created files in the editor.",
4937                field: Box::new(SettingField {
4938                    json_path: Some("project_panel.auto_open.on_create"),
4939                    pick: |settings_content| {
4940                        settings_content
4941                            .project_panel
4942                            .as_ref()?
4943                            .auto_open
4944                            .as_ref()?
4945                            .on_create
4946                            .as_ref()
4947                    },
4948                    write: |settings_content, value| {
4949                        settings_content
4950                            .project_panel
4951                            .get_or_insert_default()
4952                            .auto_open
4953                            .get_or_insert_default()
4954                            .on_create = value;
4955                    },
4956                }),
4957                metadata: None,
4958                files: USER,
4959            }),
4960            SettingsPageItem::SettingItem(SettingItem {
4961                title: "Auto Open Files On Paste",
4962                description: "Whether to automatically open files after pasting or duplicating them.",
4963                field: Box::new(SettingField {
4964                    json_path: Some("project_panel.auto_open.on_paste"),
4965                    pick: |settings_content| {
4966                        settings_content
4967                            .project_panel
4968                            .as_ref()?
4969                            .auto_open
4970                            .as_ref()?
4971                            .on_paste
4972                            .as_ref()
4973                    },
4974                    write: |settings_content, value| {
4975                        settings_content
4976                            .project_panel
4977                            .get_or_insert_default()
4978                            .auto_open
4979                            .get_or_insert_default()
4980                            .on_paste = value;
4981                    },
4982                }),
4983                metadata: None,
4984                files: USER,
4985            }),
4986            SettingsPageItem::SettingItem(SettingItem {
4987                title: "Auto Open Files On Drop",
4988                description: "Whether to automatically open files dropped from external sources.",
4989                field: Box::new(SettingField {
4990                    json_path: Some("project_panel.auto_open.on_drop"),
4991                    pick: |settings_content| {
4992                        settings_content
4993                            .project_panel
4994                            .as_ref()?
4995                            .auto_open
4996                            .as_ref()?
4997                            .on_drop
4998                            .as_ref()
4999                    },
5000                    write: |settings_content, value| {
5001                        settings_content
5002                            .project_panel
5003                            .get_or_insert_default()
5004                            .auto_open
5005                            .get_or_insert_default()
5006                            .on_drop = value;
5007                    },
5008                }),
5009                metadata: None,
5010                files: USER,
5011            }),
5012            SettingsPageItem::SettingItem(SettingItem {
5013                title: "Hidden Files",
5014                description: "Globs to match files that will be considered \"hidden\" and can be hidden from the project panel.",
5015                field: Box::new(
5016                    SettingField {
5017                        json_path: Some("worktree.hidden_files"),
5018                        pick: |settings_content| {
5019                            settings_content.project.worktree.hidden_files.as_ref()
5020                        },
5021                        write: |settings_content, value| {
5022                            settings_content.project.worktree.hidden_files = value;
5023                        },
5024                    }
5025                    .unimplemented(),
5026                ),
5027                metadata: None,
5028                files: USER,
5029            }),
5030        ]
5031    }
5032
5033    fn terminal_panel_section() -> [SettingsPageItem; 4] {
5034        [
5035            SettingsPageItem::SectionHeader("Terminal Panel"),
5036            SettingsPageItem::SettingItem(SettingItem {
5037                title: "Terminal Dock",
5038                description: "Where to dock the terminal panel.",
5039                field: Box::new(SettingField {
5040                    json_path: Some("terminal.dock"),
5041                    pick: |settings_content| settings_content.terminal.as_ref()?.dock.as_ref(),
5042                    write: |settings_content, value| {
5043                        settings_content.terminal.get_or_insert_default().dock = value;
5044                    },
5045                }),
5046                metadata: None,
5047                files: USER,
5048            }),
5049            SettingsPageItem::SettingItem(SettingItem {
5050                title: "Terminal Panel Flexible Sizing",
5051                description: "Whether the terminal panel should use flexible (proportional) sizing when docked to the left or right.",
5052                field: Box::new(SettingField {
5053                    json_path: Some("terminal.flexible"),
5054                    pick: |settings_content| settings_content.terminal.as_ref()?.flexible.as_ref(),
5055                    write: |settings_content, value| {
5056                        settings_content.terminal.get_or_insert_default().flexible = value;
5057                    },
5058                }),
5059                metadata: None,
5060                files: USER,
5061            }),
5062            SettingsPageItem::SettingItem(SettingItem {
5063                title: "Show Count Badge",
5064                description: "Show a badge on the terminal panel icon with the count of open terminals.",
5065                field: Box::new(SettingField {
5066                    json_path: Some("terminal.show_count_badge"),
5067                    pick: |settings_content| {
5068                        settings_content
5069                            .terminal
5070                            .as_ref()?
5071                            .show_count_badge
5072                            .as_ref()
5073                    },
5074                    write: |settings_content, value| {
5075                        settings_content
5076                            .terminal
5077                            .get_or_insert_default()
5078                            .show_count_badge = value;
5079                    },
5080                }),
5081                metadata: None,
5082                files: USER,
5083            }),
5084        ]
5085    }
5086
5087    fn outline_panel_section() -> [SettingsPageItem; 11] {
5088        [
5089            SettingsPageItem::SectionHeader("Outline Panel"),
5090            SettingsPageItem::SettingItem(SettingItem {
5091                title: "Outline Panel Button",
5092                description: "Show the outline panel button in the status bar.",
5093                field: Box::new(SettingField {
5094                    json_path: Some("outline_panel.button"),
5095                    pick: |settings_content| {
5096                        settings_content.outline_panel.as_ref()?.button.as_ref()
5097                    },
5098                    write: |settings_content, value| {
5099                        settings_content
5100                            .outline_panel
5101                            .get_or_insert_default()
5102                            .button = value;
5103                    },
5104                }),
5105                metadata: None,
5106                files: USER,
5107            }),
5108            SettingsPageItem::SettingItem(SettingItem {
5109                title: "Outline Panel Dock",
5110                description: "Where to dock the outline panel.",
5111                field: Box::new(SettingField {
5112                    json_path: Some("outline_panel.dock"),
5113                    pick: |settings_content| settings_content.outline_panel.as_ref()?.dock.as_ref(),
5114                    write: |settings_content, value| {
5115                        settings_content.outline_panel.get_or_insert_default().dock = value;
5116                    },
5117                }),
5118                metadata: None,
5119                files: USER,
5120            }),
5121            SettingsPageItem::SettingItem(SettingItem {
5122                title: "Outline Panel Default Width",
5123                description: "Default width of the outline panel in pixels.",
5124                field: Box::new(SettingField {
5125                    json_path: Some("outline_panel.default_width"),
5126                    pick: |settings_content| {
5127                        settings_content
5128                            .outline_panel
5129                            .as_ref()?
5130                            .default_width
5131                            .as_ref()
5132                    },
5133                    write: |settings_content, value| {
5134                        settings_content
5135                            .outline_panel
5136                            .get_or_insert_default()
5137                            .default_width = value;
5138                    },
5139                }),
5140                metadata: None,
5141                files: USER,
5142            }),
5143            SettingsPageItem::SettingItem(SettingItem {
5144                title: "File Icons",
5145                description: "Show file icons in the outline panel.",
5146                field: Box::new(SettingField {
5147                    json_path: Some("outline_panel.file_icons"),
5148                    pick: |settings_content| {
5149                        settings_content.outline_panel.as_ref()?.file_icons.as_ref()
5150                    },
5151                    write: |settings_content, value| {
5152                        settings_content
5153                            .outline_panel
5154                            .get_or_insert_default()
5155                            .file_icons = value;
5156                    },
5157                }),
5158                metadata: None,
5159                files: USER,
5160            }),
5161            SettingsPageItem::SettingItem(SettingItem {
5162                title: "Folder Icons",
5163                description: "Whether to show folder icons or chevrons for directories in the outline panel.",
5164                field: Box::new(SettingField {
5165                    json_path: Some("outline_panel.folder_icons"),
5166                    pick: |settings_content| {
5167                        settings_content
5168                            .outline_panel
5169                            .as_ref()?
5170                            .folder_icons
5171                            .as_ref()
5172                    },
5173                    write: |settings_content, value| {
5174                        settings_content
5175                            .outline_panel
5176                            .get_or_insert_default()
5177                            .folder_icons = value;
5178                    },
5179                }),
5180                metadata: None,
5181                files: USER,
5182            }),
5183            SettingsPageItem::SettingItem(SettingItem {
5184                title: "Git Status",
5185                description: "Show the Git status in the outline panel.",
5186                field: Box::new(SettingField {
5187                    json_path: Some("outline_panel.git_status"),
5188                    pick: |settings_content| {
5189                        settings_content.outline_panel.as_ref()?.git_status.as_ref()
5190                    },
5191                    write: |settings_content, value| {
5192                        settings_content
5193                            .outline_panel
5194                            .get_or_insert_default()
5195                            .git_status = value;
5196                    },
5197                }),
5198                metadata: None,
5199                files: USER,
5200            }),
5201            SettingsPageItem::SettingItem(SettingItem {
5202                title: "Indent Size",
5203                description: "Amount of indentation for nested items.",
5204                field: Box::new(SettingField {
5205                    json_path: Some("outline_panel.indent_size"),
5206                    pick: |settings_content| {
5207                        settings_content
5208                            .outline_panel
5209                            .as_ref()?
5210                            .indent_size
5211                            .as_ref()
5212                    },
5213                    write: |settings_content, value| {
5214                        settings_content
5215                            .outline_panel
5216                            .get_or_insert_default()
5217                            .indent_size = value;
5218                    },
5219                }),
5220                metadata: None,
5221                files: USER,
5222            }),
5223            SettingsPageItem::SettingItem(SettingItem {
5224                title: "Auto Reveal Entries",
5225                description: "Whether to reveal when a corresponding outline entry becomes active.",
5226                field: Box::new(SettingField {
5227                    json_path: Some("outline_panel.auto_reveal_entries"),
5228                    pick: |settings_content| {
5229                        settings_content
5230                            .outline_panel
5231                            .as_ref()?
5232                            .auto_reveal_entries
5233                            .as_ref()
5234                    },
5235                    write: |settings_content, value| {
5236                        settings_content
5237                            .outline_panel
5238                            .get_or_insert_default()
5239                            .auto_reveal_entries = value;
5240                    },
5241                }),
5242                metadata: None,
5243                files: USER,
5244            }),
5245            SettingsPageItem::SettingItem(SettingItem {
5246                title: "Auto Fold Directories",
5247                description: "Whether to fold directories automatically when a directory contains only one subdirectory.",
5248                field: Box::new(SettingField {
5249                    json_path: Some("outline_panel.auto_fold_dirs"),
5250                    pick: |settings_content| {
5251                        settings_content
5252                            .outline_panel
5253                            .as_ref()?
5254                            .auto_fold_dirs
5255                            .as_ref()
5256                    },
5257                    write: |settings_content, value| {
5258                        settings_content
5259                            .outline_panel
5260                            .get_or_insert_default()
5261                            .auto_fold_dirs = value;
5262                    },
5263                }),
5264                metadata: None,
5265                files: USER,
5266            }),
5267            SettingsPageItem::SettingItem(SettingItem {
5268                files: USER,
5269                title: "Show Indent Guides",
5270                description: "When to show indent guides in the outline panel.",
5271                field: Box::new(SettingField {
5272                    json_path: Some("outline_panel.indent_guides.show"),
5273                    pick: |settings_content| {
5274                        settings_content
5275                            .outline_panel
5276                            .as_ref()?
5277                            .indent_guides
5278                            .as_ref()?
5279                            .show
5280                            .as_ref()
5281                    },
5282                    write: |settings_content, value| {
5283                        settings_content
5284                            .outline_panel
5285                            .get_or_insert_default()
5286                            .indent_guides
5287                            .get_or_insert_default()
5288                            .show = value;
5289                    },
5290                }),
5291                metadata: None,
5292            }),
5293        ]
5294    }
5295
5296    fn git_panel_section() -> [SettingsPageItem; 14] {
5297        [
5298            SettingsPageItem::SectionHeader("Git Panel"),
5299            SettingsPageItem::SettingItem(SettingItem {
5300                title: "Git Panel Button",
5301                description: "Show the Git panel button in the status bar.",
5302                field: Box::new(SettingField {
5303                    json_path: Some("git_panel.button"),
5304                    pick: |settings_content| settings_content.git_panel.as_ref()?.button.as_ref(),
5305                    write: |settings_content, value| {
5306                        settings_content.git_panel.get_or_insert_default().button = value;
5307                    },
5308                }),
5309                metadata: None,
5310                files: USER,
5311            }),
5312            SettingsPageItem::SettingItem(SettingItem {
5313                title: "Git Panel Dock",
5314                description: "Where to dock the Git panel.",
5315                field: Box::new(SettingField {
5316                    json_path: Some("git_panel.dock"),
5317                    pick: |settings_content| settings_content.git_panel.as_ref()?.dock.as_ref(),
5318                    write: |settings_content, value| {
5319                        settings_content.git_panel.get_or_insert_default().dock = value;
5320                    },
5321                }),
5322                metadata: None,
5323                files: USER,
5324            }),
5325            SettingsPageItem::SettingItem(SettingItem {
5326                title: "Git Panel Default Width",
5327                description: "Default width of the Git panel in pixels.",
5328                field: Box::new(SettingField {
5329                    json_path: Some("git_panel.default_width"),
5330                    pick: |settings_content| {
5331                        settings_content.git_panel.as_ref()?.default_width.as_ref()
5332                    },
5333                    write: |settings_content, value| {
5334                        settings_content
5335                            .git_panel
5336                            .get_or_insert_default()
5337                            .default_width = value;
5338                    },
5339                }),
5340                metadata: None,
5341                files: USER,
5342            }),
5343            SettingsPageItem::SettingItem(SettingItem {
5344                title: "Git Panel Status Style",
5345                description: "How entry statuses are displayed.",
5346                field: Box::new(SettingField {
5347                    json_path: Some("git_panel.status_style"),
5348                    pick: |settings_content| {
5349                        settings_content.git_panel.as_ref()?.status_style.as_ref()
5350                    },
5351                    write: |settings_content, value| {
5352                        settings_content
5353                            .git_panel
5354                            .get_or_insert_default()
5355                            .status_style = value;
5356                    },
5357                }),
5358                metadata: None,
5359                files: USER,
5360            }),
5361            SettingsPageItem::SettingItem(SettingItem {
5362                title: "Fallback Branch Name",
5363                description: "Default branch name will be when init.defaultbranch is not set in Git.",
5364                field: Box::new(SettingField {
5365                    json_path: Some("git_panel.fallback_branch_name"),
5366                    pick: |settings_content| {
5367                        settings_content
5368                            .git_panel
5369                            .as_ref()?
5370                            .fallback_branch_name
5371                            .as_ref()
5372                    },
5373                    write: |settings_content, value| {
5374                        settings_content
5375                            .git_panel
5376                            .get_or_insert_default()
5377                            .fallback_branch_name = value;
5378                    },
5379                }),
5380                metadata: None,
5381                files: USER,
5382            }),
5383            SettingsPageItem::SettingItem(SettingItem {
5384                title: "Sort By Path",
5385                description: "Enable to sort entries in the panel by path, disable to sort by status.",
5386                field: Box::new(SettingField {
5387                    json_path: Some("git_panel.sort_by_path"),
5388                    pick: |settings_content| {
5389                        settings_content.git_panel.as_ref()?.sort_by_path.as_ref()
5390                    },
5391                    write: |settings_content, value| {
5392                        settings_content
5393                            .git_panel
5394                            .get_or_insert_default()
5395                            .sort_by_path = value;
5396                    },
5397                }),
5398                metadata: None,
5399                files: USER,
5400            }),
5401            SettingsPageItem::SettingItem(SettingItem {
5402                title: "Collapse Untracked Diff",
5403                description: "Whether to collapse untracked files in the diff panel.",
5404                field: Box::new(SettingField {
5405                    json_path: Some("git_panel.collapse_untracked_diff"),
5406                    pick: |settings_content| {
5407                        settings_content
5408                            .git_panel
5409                            .as_ref()?
5410                            .collapse_untracked_diff
5411                            .as_ref()
5412                    },
5413                    write: |settings_content, value| {
5414                        settings_content
5415                            .git_panel
5416                            .get_or_insert_default()
5417                            .collapse_untracked_diff = value;
5418                    },
5419                }),
5420                metadata: None,
5421                files: USER,
5422            }),
5423            SettingsPageItem::SettingItem(SettingItem {
5424                title: "Tree View",
5425                description: "Enable to show entries in tree view list, disable to show in flat view list.",
5426                field: Box::new(SettingField {
5427                    json_path: Some("git_panel.tree_view"),
5428                    pick: |settings_content| {
5429                        settings_content.git_panel.as_ref()?.tree_view.as_ref()
5430                    },
5431                    write: |settings_content, value| {
5432                        settings_content.git_panel.get_or_insert_default().tree_view = value;
5433                    },
5434                }),
5435                metadata: None,
5436                files: USER,
5437            }),
5438            SettingsPageItem::SettingItem(SettingItem {
5439                title: "File Icons",
5440                description: "Show file icons next to the Git status icon.",
5441                field: Box::new(SettingField {
5442                    json_path: Some("git_panel.file_icons"),
5443                    pick: |settings_content| {
5444                        settings_content.git_panel.as_ref()?.file_icons.as_ref()
5445                    },
5446                    write: |settings_content, value| {
5447                        settings_content
5448                            .git_panel
5449                            .get_or_insert_default()
5450                            .file_icons = value;
5451                    },
5452                }),
5453                metadata: None,
5454                files: USER,
5455            }),
5456            SettingsPageItem::SettingItem(SettingItem {
5457                title: "Folder Icons",
5458                description: "Whether to show folder icons or chevrons for directories in the git panel.",
5459                field: Box::new(SettingField {
5460                    json_path: Some("git_panel.folder_icons"),
5461                    pick: |settings_content| {
5462                        settings_content.git_panel.as_ref()?.folder_icons.as_ref()
5463                    },
5464                    write: |settings_content, value| {
5465                        settings_content
5466                            .git_panel
5467                            .get_or_insert_default()
5468                            .folder_icons = value;
5469                    },
5470                }),
5471                metadata: None,
5472                files: USER,
5473            }),
5474            SettingsPageItem::SettingItem(SettingItem {
5475                title: "Diff Stats",
5476                description: "Whether to show the addition/deletion change count next to each file in the Git panel.",
5477                field: Box::new(SettingField {
5478                    json_path: Some("git_panel.diff_stats"),
5479                    pick: |settings_content| {
5480                        settings_content.git_panel.as_ref()?.diff_stats.as_ref()
5481                    },
5482                    write: |settings_content, value| {
5483                        settings_content
5484                            .git_panel
5485                            .get_or_insert_default()
5486                            .diff_stats = value;
5487                    },
5488                }),
5489                metadata: None,
5490                files: USER,
5491            }),
5492            SettingsPageItem::SettingItem(SettingItem {
5493                title: "Show Count Badge",
5494                description: "Whether to show a badge on the git panel icon with the count of uncommitted changes.",
5495                field: Box::new(SettingField {
5496                    json_path: Some("git_panel.show_count_badge"),
5497                    pick: |settings_content| {
5498                        settings_content
5499                            .git_panel
5500                            .as_ref()?
5501                            .show_count_badge
5502                            .as_ref()
5503                    },
5504                    write: |settings_content, value| {
5505                        settings_content
5506                            .git_panel
5507                            .get_or_insert_default()
5508                            .show_count_badge = value;
5509                    },
5510                }),
5511                metadata: None,
5512                files: USER,
5513            }),
5514            SettingsPageItem::SettingItem(SettingItem {
5515                title: "Scroll Bar",
5516                description: "How and when the scrollbar should be displayed.",
5517                field: Box::new(SettingField {
5518                    json_path: Some("git_panel.scrollbar.show"),
5519                    pick: |settings_content| {
5520                        show_scrollbar_or_editor(settings_content, |settings_content| {
5521                            settings_content
5522                                .git_panel
5523                                .as_ref()?
5524                                .scrollbar
5525                                .as_ref()?
5526                                .show
5527                                .as_ref()
5528                        })
5529                    },
5530                    write: |settings_content, value| {
5531                        settings_content
5532                            .git_panel
5533                            .get_or_insert_default()
5534                            .scrollbar
5535                            .get_or_insert_default()
5536                            .show = value;
5537                    },
5538                }),
5539                metadata: None,
5540                files: USER,
5541            }),
5542        ]
5543    }
5544
5545    fn debugger_panel_section() -> [SettingsPageItem; 2] {
5546        [
5547            SettingsPageItem::SectionHeader("Debugger Panel"),
5548            SettingsPageItem::SettingItem(SettingItem {
5549                title: "Debugger Panel Dock",
5550                description: "The dock position of the debug panel.",
5551                field: Box::new(SettingField {
5552                    json_path: Some("debugger.dock"),
5553                    pick: |settings_content| settings_content.debugger.as_ref()?.dock.as_ref(),
5554                    write: |settings_content, value| {
5555                        settings_content.debugger.get_or_insert_default().dock = value;
5556                    },
5557                }),
5558                metadata: None,
5559                files: USER,
5560            }),
5561        ]
5562    }
5563
5564    fn notification_panel_section() -> [SettingsPageItem; 5] {
5565        [
5566            SettingsPageItem::SectionHeader("Notification Panel"),
5567            SettingsPageItem::SettingItem(SettingItem {
5568                title: "Notification Panel Button",
5569                description: "Show the notification panel button in the status bar.",
5570                field: Box::new(SettingField {
5571                    json_path: Some("notification_panel.button"),
5572                    pick: |settings_content| {
5573                        settings_content
5574                            .notification_panel
5575                            .as_ref()?
5576                            .button
5577                            .as_ref()
5578                    },
5579                    write: |settings_content, value| {
5580                        settings_content
5581                            .notification_panel
5582                            .get_or_insert_default()
5583                            .button = value;
5584                    },
5585                }),
5586                metadata: None,
5587                files: USER,
5588            }),
5589            SettingsPageItem::SettingItem(SettingItem {
5590                title: "Notification Panel Dock",
5591                description: "Where to dock the notification panel.",
5592                field: Box::new(SettingField {
5593                    json_path: Some("notification_panel.dock"),
5594                    pick: |settings_content| {
5595                        settings_content.notification_panel.as_ref()?.dock.as_ref()
5596                    },
5597                    write: |settings_content, value| {
5598                        settings_content
5599                            .notification_panel
5600                            .get_or_insert_default()
5601                            .dock = value;
5602                    },
5603                }),
5604                metadata: None,
5605                files: USER,
5606            }),
5607            SettingsPageItem::SettingItem(SettingItem {
5608                title: "Notification Panel Default Width",
5609                description: "Default width of the notification panel in pixels.",
5610                field: Box::new(SettingField {
5611                    json_path: Some("notification_panel.default_width"),
5612                    pick: |settings_content| {
5613                        settings_content
5614                            .notification_panel
5615                            .as_ref()?
5616                            .default_width
5617                            .as_ref()
5618                    },
5619                    write: |settings_content, value| {
5620                        settings_content
5621                            .notification_panel
5622                            .get_or_insert_default()
5623                            .default_width = value;
5624                    },
5625                }),
5626                metadata: None,
5627                files: USER,
5628            }),
5629            SettingsPageItem::SettingItem(SettingItem {
5630                title: "Show Count Badge",
5631                description: "Show a badge on the notification panel icon with the count of unread notifications.",
5632                field: Box::new(SettingField {
5633                    json_path: Some("notification_panel.show_count_badge"),
5634                    pick: |settings_content| {
5635                        settings_content
5636                            .notification_panel
5637                            .as_ref()?
5638                            .show_count_badge
5639                            .as_ref()
5640                    },
5641                    write: |settings_content, value| {
5642                        settings_content
5643                            .notification_panel
5644                            .get_or_insert_default()
5645                            .show_count_badge = value;
5646                    },
5647                }),
5648                metadata: None,
5649                files: USER,
5650            }),
5651        ]
5652    }
5653
5654    fn collaboration_panel_section() -> [SettingsPageItem; 4] {
5655        [
5656            SettingsPageItem::SectionHeader("Collaboration Panel"),
5657            SettingsPageItem::SettingItem(SettingItem {
5658                title: "Collaboration Panel Button",
5659                description: "Show the collaboration panel button in the status bar.",
5660                field: Box::new(SettingField {
5661                    json_path: Some("collaboration_panel.button"),
5662                    pick: |settings_content| {
5663                        settings_content
5664                            .collaboration_panel
5665                            .as_ref()?
5666                            .button
5667                            .as_ref()
5668                    },
5669                    write: |settings_content, value| {
5670                        settings_content
5671                            .collaboration_panel
5672                            .get_or_insert_default()
5673                            .button = value;
5674                    },
5675                }),
5676                metadata: None,
5677                files: USER,
5678            }),
5679            SettingsPageItem::SettingItem(SettingItem {
5680                title: "Collaboration Panel Dock",
5681                description: "Where to dock the collaboration panel.",
5682                field: Box::new(SettingField {
5683                    json_path: Some("collaboration_panel.dock"),
5684                    pick: |settings_content| {
5685                        settings_content.collaboration_panel.as_ref()?.dock.as_ref()
5686                    },
5687                    write: |settings_content, value| {
5688                        settings_content
5689                            .collaboration_panel
5690                            .get_or_insert_default()
5691                            .dock = value;
5692                    },
5693                }),
5694                metadata: None,
5695                files: USER,
5696            }),
5697            SettingsPageItem::SettingItem(SettingItem {
5698                title: "Collaboration Panel Default Width",
5699                description: "Default width of the collaboration panel in pixels.",
5700                field: Box::new(SettingField {
5701                    json_path: Some("collaboration_panel.dock"),
5702                    pick: |settings_content| {
5703                        settings_content
5704                            .collaboration_panel
5705                            .as_ref()?
5706                            .default_width
5707                            .as_ref()
5708                    },
5709                    write: |settings_content, value| {
5710                        settings_content
5711                            .collaboration_panel
5712                            .get_or_insert_default()
5713                            .default_width = value;
5714                    },
5715                }),
5716                metadata: None,
5717                files: USER,
5718            }),
5719        ]
5720    }
5721
5722    fn agent_panel_section() -> [SettingsPageItem; 6] {
5723        [
5724            SettingsPageItem::SectionHeader("Agent Panel"),
5725            SettingsPageItem::SettingItem(SettingItem {
5726                title: "Agent Panel Button",
5727                description: "Whether to show the agent panel button in the status bar.",
5728                field: Box::new(SettingField {
5729                    json_path: Some("agent.button"),
5730                    pick: |settings_content| settings_content.agent.as_ref()?.button.as_ref(),
5731                    write: |settings_content, value| {
5732                        settings_content.agent.get_or_insert_default().button = value;
5733                    },
5734                }),
5735                metadata: None,
5736                files: USER,
5737            }),
5738            SettingsPageItem::SettingItem(SettingItem {
5739                title: "Agent Panel Dock",
5740                description: "Where to dock the agent panel.",
5741                field: Box::new(SettingField {
5742                    json_path: Some("agent.dock"),
5743                    pick: |settings_content| settings_content.agent.as_ref()?.dock.as_ref(),
5744                    write: |settings_content, value| {
5745                        settings_content.agent.get_or_insert_default().dock = value;
5746                    },
5747                }),
5748                metadata: None,
5749                files: USER,
5750            }),
5751            SettingsPageItem::SettingItem(SettingItem {
5752                title: "Agent Panel Flexible Sizing",
5753                description: "Whether the agent panel should use flexible (proportional) sizing when docked to the left or right.",
5754                field: Box::new(SettingField {
5755                    json_path: Some("agent.flexible"),
5756                    pick: |settings_content| settings_content.agent.as_ref()?.flexible.as_ref(),
5757                    write: |settings_content, value| {
5758                        settings_content.agent.get_or_insert_default().flexible = value;
5759                    },
5760                }),
5761                metadata: None,
5762                files: USER,
5763            }),
5764            SettingsPageItem::SettingItem(SettingItem {
5765                title: "Agent Panel Default Width",
5766                description: "Default width when the agent panel is docked to the left or right.",
5767                field: Box::new(SettingField {
5768                    json_path: Some("agent.default_width"),
5769                    pick: |settings_content| {
5770                        settings_content.agent.as_ref()?.default_width.as_ref()
5771                    },
5772                    write: |settings_content, value| {
5773                        settings_content.agent.get_or_insert_default().default_width = value;
5774                    },
5775                }),
5776                metadata: None,
5777                files: USER,
5778            }),
5779            SettingsPageItem::SettingItem(SettingItem {
5780                title: "Agent Panel Default Height",
5781                description: "Default height when the agent panel is docked to the bottom.",
5782                field: Box::new(SettingField {
5783                    json_path: Some("agent.default_height"),
5784                    pick: |settings_content| {
5785                        settings_content.agent.as_ref()?.default_height.as_ref()
5786                    },
5787                    write: |settings_content, value| {
5788                        settings_content
5789                            .agent
5790                            .get_or_insert_default()
5791                            .default_height = value;
5792                    },
5793                }),
5794                metadata: None,
5795                files: USER,
5796            }),
5797        ]
5798    }
5799
5800    SettingsPage {
5801        title: "Panels",
5802        items: concat_sections![
5803            project_panel_section(),
5804            terminal_panel_section(),
5805            outline_panel_section(),
5806            git_panel_section(),
5807            debugger_panel_section(),
5808            notification_panel_section(),
5809            collaboration_panel_section(),
5810            agent_panel_section(),
5811        ],
5812    }
5813}
5814
5815fn debugger_page() -> SettingsPage {
5816    fn general_section() -> [SettingsPageItem; 6] {
5817        [
5818            SettingsPageItem::SectionHeader("General"),
5819            SettingsPageItem::SettingItem(SettingItem {
5820                title: "Stepping Granularity",
5821                description: "Determines the stepping granularity for debug operations.",
5822                field: Box::new(SettingField {
5823                    json_path: Some("debugger.stepping_granularity"),
5824                    pick: |settings_content| {
5825                        settings_content
5826                            .debugger
5827                            .as_ref()?
5828                            .stepping_granularity
5829                            .as_ref()
5830                    },
5831                    write: |settings_content, value| {
5832                        settings_content
5833                            .debugger
5834                            .get_or_insert_default()
5835                            .stepping_granularity = value;
5836                    },
5837                }),
5838                metadata: None,
5839                files: USER,
5840            }),
5841            SettingsPageItem::SettingItem(SettingItem {
5842                title: "Save Breakpoints",
5843                description: "Whether breakpoints should be reused across Zed sessions.",
5844                field: Box::new(SettingField {
5845                    json_path: Some("debugger.save_breakpoints"),
5846                    pick: |settings_content| {
5847                        settings_content
5848                            .debugger
5849                            .as_ref()?
5850                            .save_breakpoints
5851                            .as_ref()
5852                    },
5853                    write: |settings_content, value| {
5854                        settings_content
5855                            .debugger
5856                            .get_or_insert_default()
5857                            .save_breakpoints = value;
5858                    },
5859                }),
5860                metadata: None,
5861                files: USER,
5862            }),
5863            SettingsPageItem::SettingItem(SettingItem {
5864                title: "Timeout",
5865                description: "Time in milliseconds until timeout error when connecting to a TCP debug adapter.",
5866                field: Box::new(SettingField {
5867                    json_path: Some("debugger.timeout"),
5868                    pick: |settings_content| settings_content.debugger.as_ref()?.timeout.as_ref(),
5869                    write: |settings_content, value| {
5870                        settings_content.debugger.get_or_insert_default().timeout = value;
5871                    },
5872                }),
5873                metadata: None,
5874                files: USER,
5875            }),
5876            SettingsPageItem::SettingItem(SettingItem {
5877                title: "Log DAP Communications",
5878                description: "Whether to log messages between active debug adapters and Zed.",
5879                field: Box::new(SettingField {
5880                    json_path: Some("debugger.log_dap_communications"),
5881                    pick: |settings_content| {
5882                        settings_content
5883                            .debugger
5884                            .as_ref()?
5885                            .log_dap_communications
5886                            .as_ref()
5887                    },
5888                    write: |settings_content, value| {
5889                        settings_content
5890                            .debugger
5891                            .get_or_insert_default()
5892                            .log_dap_communications = value;
5893                    },
5894                }),
5895                metadata: None,
5896                files: USER,
5897            }),
5898            SettingsPageItem::SettingItem(SettingItem {
5899                title: "Format DAP Log Messages",
5900                description: "Whether to format DAP messages when adding them to debug adapter logger.",
5901                field: Box::new(SettingField {
5902                    json_path: Some("debugger.format_dap_log_messages"),
5903                    pick: |settings_content| {
5904                        settings_content
5905                            .debugger
5906                            .as_ref()?
5907                            .format_dap_log_messages
5908                            .as_ref()
5909                    },
5910                    write: |settings_content, value| {
5911                        settings_content
5912                            .debugger
5913                            .get_or_insert_default()
5914                            .format_dap_log_messages = value;
5915                    },
5916                }),
5917                metadata: None,
5918                files: USER,
5919            }),
5920        ]
5921    }
5922
5923    SettingsPage {
5924        title: "Debugger",
5925        items: concat_sections![general_section()],
5926    }
5927}
5928
5929fn terminal_page() -> SettingsPage {
5930    fn environment_section() -> [SettingsPageItem; 5] {
5931        [
5932                SettingsPageItem::SectionHeader("Environment"),
5933                SettingsPageItem::DynamicItem(DynamicItem {
5934                    discriminant: SettingItem {
5935                        files: USER | PROJECT,
5936                        title: "Shell",
5937                        description: "What shell to use when opening a terminal.",
5938                        field: Box::new(SettingField {
5939                            json_path: Some("terminal.shell$"),
5940                            pick: |settings_content| {
5941                                Some(&dynamic_variants::<settings::Shell>()[
5942                                    settings_content
5943                                        .terminal
5944                                        .as_ref()?
5945                                        .project
5946                                        .shell
5947                                        .as_ref()?
5948                                        .discriminant() as usize
5949                                ])
5950                            },
5951                            write: |settings_content, value| {
5952                                let Some(value) = value else {
5953                                    if let Some(terminal) = settings_content.terminal.as_mut() {
5954                                        terminal.project.shell = None;
5955                                    }
5956                                    return;
5957                                };
5958                                let settings_value = settings_content
5959                                    .terminal
5960                                    .get_or_insert_default()
5961                                    .project
5962                                    .shell
5963                                    .get_or_insert_with(|| settings::Shell::default());
5964                                let default_shell = if cfg!(target_os = "windows") {
5965                                    "powershell.exe"
5966                                } else {
5967                                    "sh"
5968                                };
5969                                *settings_value = match value {
5970                                    settings::ShellDiscriminants::System => settings::Shell::System,
5971                                    settings::ShellDiscriminants::Program => {
5972                                        let program = match settings_value {
5973                                            settings::Shell::Program(program) => program.clone(),
5974                                            settings::Shell::WithArguments { program, .. } => program.clone(),
5975                                            _ => String::from(default_shell),
5976                                        };
5977                                        settings::Shell::Program(program)
5978                                    }
5979                                    settings::ShellDiscriminants::WithArguments => {
5980                                        let (program, args, title_override) = match settings_value {
5981                                            settings::Shell::Program(program) => (program.clone(), vec![], None),
5982                                            settings::Shell::WithArguments {
5983                                                program,
5984                                                args,
5985                                                title_override,
5986                                            } => (program.clone(), args.clone(), title_override.clone()),
5987                                            _ => (String::from(default_shell), vec![], None),
5988                                        };
5989                                        settings::Shell::WithArguments {
5990                                            program,
5991                                            args,
5992                                            title_override,
5993                                        }
5994                                    }
5995                                };
5996                            },
5997                        }),
5998                        metadata: None,
5999                    },
6000                    pick_discriminant: |settings_content| {
6001                        Some(
6002                            settings_content
6003                                .terminal
6004                                .as_ref()?
6005                                .project
6006                                .shell
6007                                .as_ref()?
6008                                .discriminant() as usize,
6009                        )
6010                    },
6011                    fields: dynamic_variants::<settings::Shell>()
6012                        .into_iter()
6013                        .map(|variant| match variant {
6014                            settings::ShellDiscriminants::System => vec![],
6015                            settings::ShellDiscriminants::Program => vec![SettingItem {
6016                                files: USER | PROJECT,
6017                                title: "Program",
6018                                description: "The shell program to use.",
6019                                field: Box::new(SettingField {
6020                                    json_path: Some("terminal.shell"),
6021                                    pick: |settings_content| match settings_content.terminal.as_ref()?.project.shell.as_ref()
6022                                    {
6023                                        Some(settings::Shell::Program(program)) => Some(program),
6024                                        _ => None,
6025                                    },
6026                                    write: |settings_content, value| {
6027                                        let Some(value) = value else {
6028                                            return;
6029                                        };
6030                                        match settings_content
6031                                            .terminal
6032                                            .get_or_insert_default()
6033                                            .project
6034                                            .shell
6035                                            .as_mut()
6036                                        {
6037                                            Some(settings::Shell::Program(program)) => *program = value,
6038                                            _ => return,
6039                                        }
6040                                    },
6041                                }),
6042                                metadata: None,
6043                            }],
6044                            settings::ShellDiscriminants::WithArguments => vec![
6045                                SettingItem {
6046                                    files: USER | PROJECT,
6047                                    title: "Program",
6048                                    description: "The shell program to run.",
6049                                    field: Box::new(SettingField {
6050                                        json_path: Some("terminal.shell.program"),
6051                                        pick: |settings_content| {
6052                                            match settings_content.terminal.as_ref()?.project.shell.as_ref() {
6053                                                Some(settings::Shell::WithArguments { program, .. }) => Some(program),
6054                                                _ => None,
6055                                            }
6056                                        },
6057                                        write: |settings_content, value| {
6058                                            let Some(value) = value else {
6059                                                return;
6060                                            };
6061                                            match settings_content
6062                                                .terminal
6063                                                .get_or_insert_default()
6064                                                .project
6065                                                .shell
6066                                                .as_mut()
6067                                            {
6068                                                Some(settings::Shell::WithArguments { program, .. }) => {
6069                                                    *program = value
6070                                                }
6071                                                _ => return,
6072                                            }
6073                                        },
6074                                    }),
6075                                    metadata: None,
6076                                },
6077                                SettingItem {
6078                                    files: USER | PROJECT,
6079                                    title: "Arguments",
6080                                    description: "The arguments to pass to the shell program.",
6081                                    field: Box::new(
6082                                        SettingField {
6083                                            json_path: Some("terminal.shell.args"),
6084                                            pick: |settings_content| {
6085                                                match settings_content.terminal.as_ref()?.project.shell.as_ref() {
6086                                                    Some(settings::Shell::WithArguments { args, .. }) => Some(args),
6087                                                    _ => None,
6088                                                }
6089                                            },
6090                                            write: |settings_content, value| {
6091                                                let Some(value) = value else {
6092                                                    return;
6093                                                };
6094                                                match settings_content
6095                                                    .terminal
6096                                                    .get_or_insert_default()
6097                                                    .project
6098                                                    .shell
6099                                                    .as_mut()
6100                                                {
6101                                                    Some(settings::Shell::WithArguments { args, .. }) => *args = value,
6102                                                    _ => return,
6103                                                }
6104                                            },
6105                                        }
6106                                        .unimplemented(),
6107                                    ),
6108                                    metadata: None,
6109                                },
6110                                SettingItem {
6111                                    files: USER | PROJECT,
6112                                    title: "Title Override",
6113                                    description: "An optional string to override the title of the terminal tab.",
6114                                    field: Box::new(SettingField {
6115                                        json_path: Some("terminal.shell.title_override"),
6116                                        pick: |settings_content| {
6117                                            match settings_content.terminal.as_ref()?.project.shell.as_ref() {
6118                                                Some(settings::Shell::WithArguments { title_override, .. }) => {
6119                                                    title_override.as_ref().or(DEFAULT_EMPTY_STRING)
6120                                                }
6121                                                _ => None,
6122                                            }
6123                                        },
6124                                        write: |settings_content, value| {
6125                                            match settings_content
6126                                                .terminal
6127                                                .get_or_insert_default()
6128                                                .project
6129                                                .shell
6130                                                .as_mut()
6131                                            {
6132                                                Some(settings::Shell::WithArguments { title_override, .. }) => {
6133                                                    *title_override = value.filter(|s| !s.is_empty())
6134                                                }
6135                                                _ => return,
6136                                            }
6137                                        },
6138                                    }),
6139                                    metadata: None,
6140                                },
6141                            ],
6142                        })
6143                        .collect(),
6144                }),
6145                SettingsPageItem::DynamicItem(DynamicItem {
6146                    discriminant: SettingItem {
6147                        files: USER | PROJECT,
6148                        title: "Working Directory",
6149                        description: "What working directory to use when launching the terminal.",
6150                        field: Box::new(SettingField {
6151                            json_path: Some("terminal.working_directory$"),
6152                            pick: |settings_content| {
6153                                Some(&dynamic_variants::<settings::WorkingDirectory>()[
6154                                    settings_content
6155                                        .terminal
6156                                        .as_ref()?
6157                                        .project
6158                                        .working_directory
6159                                        .as_ref()?
6160                                        .discriminant() as usize
6161                                ])
6162                            },
6163                            write: |settings_content, value| {
6164                                let Some(value) = value else {
6165                                    if let Some(terminal) = settings_content.terminal.as_mut() {
6166                                        terminal.project.working_directory = None;
6167                                    }
6168                                    return;
6169                                };
6170                                let settings_value = settings_content
6171                                    .terminal
6172                                    .get_or_insert_default()
6173                                    .project
6174                                    .working_directory
6175                                    .get_or_insert_with(|| settings::WorkingDirectory::CurrentProjectDirectory);
6176                                *settings_value = match value {
6177                                    settings::WorkingDirectoryDiscriminants::CurrentFileDirectory => {
6178                                        settings::WorkingDirectory::CurrentFileDirectory
6179                                    },
6180                                    settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => {
6181                                        settings::WorkingDirectory::CurrentProjectDirectory
6182                                    }
6183                                    settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => {
6184                                        settings::WorkingDirectory::FirstProjectDirectory
6185                                    }
6186                                    settings::WorkingDirectoryDiscriminants::AlwaysHome => {
6187                                        settings::WorkingDirectory::AlwaysHome
6188                                    }
6189                                    settings::WorkingDirectoryDiscriminants::Always => {
6190                                        let directory = match settings_value {
6191                                            settings::WorkingDirectory::Always { .. } => return,
6192                                            _ => String::new(),
6193                                        };
6194                                        settings::WorkingDirectory::Always { directory }
6195                                    }
6196                                };
6197                            },
6198                        }),
6199                        metadata: None,
6200                    },
6201                    pick_discriminant: |settings_content| {
6202                        Some(
6203                            settings_content
6204                                .terminal
6205                                .as_ref()?
6206                                .project
6207                                .working_directory
6208                                .as_ref()?
6209                                .discriminant() as usize,
6210                        )
6211                    },
6212                    fields: dynamic_variants::<settings::WorkingDirectory>()
6213                        .into_iter()
6214                        .map(|variant| match variant {
6215                            settings::WorkingDirectoryDiscriminants::CurrentFileDirectory => vec![],
6216                            settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => vec![],
6217                            settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => vec![],
6218                            settings::WorkingDirectoryDiscriminants::AlwaysHome => vec![],
6219                            settings::WorkingDirectoryDiscriminants::Always => vec![SettingItem {
6220                                files: USER | PROJECT,
6221                                title: "Directory",
6222                                description: "The directory path to use (will be shell expanded).",
6223                                field: Box::new(SettingField {
6224                                    json_path: Some("terminal.working_directory.always"),
6225                                    pick: |settings_content| {
6226                                        match settings_content.terminal.as_ref()?.project.working_directory.as_ref() {
6227                                            Some(settings::WorkingDirectory::Always { directory }) => Some(directory),
6228                                            _ => None,
6229                                        }
6230                                    },
6231                                    write: |settings_content, value| {
6232                                        let value = value.unwrap_or_default();
6233                                        match settings_content
6234                                            .terminal
6235                                            .get_or_insert_default()
6236                                            .project
6237                                            .working_directory
6238                                            .as_mut()
6239                                        {
6240                                            Some(settings::WorkingDirectory::Always { directory }) => *directory = value,
6241                                            _ => return,
6242                                        }
6243                                    },
6244                                }),
6245                                metadata: None,
6246                            }],
6247                        })
6248                        .collect(),
6249                }),
6250                SettingsPageItem::SettingItem(SettingItem {
6251                    title: "Environment Variables",
6252                    description: "Key-value pairs to add to the terminal's environment.",
6253                    field: Box::new(
6254                        SettingField {
6255                            json_path: Some("terminal.env"),
6256                            pick: |settings_content| settings_content.terminal.as_ref()?.project.env.as_ref(),
6257                            write: |settings_content, value| {
6258                                settings_content.terminal.get_or_insert_default().project.env = value;
6259                            },
6260                        }
6261                        .unimplemented(),
6262                    ),
6263                    metadata: None,
6264                    files: USER | PROJECT,
6265                }),
6266                SettingsPageItem::SettingItem(SettingItem {
6267                    title: "Detect Virtual Environment",
6268                    description: "Activates the Python virtual environment, if one is found, in the terminal's working directory.",
6269                    field: Box::new(
6270                        SettingField {
6271                            json_path: Some("terminal.detect_venv"),
6272                            pick: |settings_content| settings_content.terminal.as_ref()?.project.detect_venv.as_ref(),
6273                            write: |settings_content, value| {
6274                                settings_content
6275                                    .terminal
6276                                    .get_or_insert_default()
6277                                    .project
6278                                    .detect_venv = value;
6279                            },
6280                        }
6281                        .unimplemented(),
6282                    ),
6283                    metadata: None,
6284                    files: USER | PROJECT,
6285                }),
6286            ]
6287    }
6288
6289    fn font_section() -> [SettingsPageItem; 6] {
6290        [
6291            SettingsPageItem::SectionHeader("Font"),
6292            SettingsPageItem::SettingItem(SettingItem {
6293                title: "Font Size",
6294                description: "Font size for terminal text. If not set, defaults to buffer font size.",
6295                field: Box::new(SettingField {
6296                    json_path: Some("terminal.font_size"),
6297                    pick: |settings_content| {
6298                        settings_content
6299                            .terminal
6300                            .as_ref()
6301                            .and_then(|terminal| terminal.font_size.as_ref())
6302                            .or(settings_content.theme.buffer_font_size.as_ref())
6303                    },
6304                    write: |settings_content, value| {
6305                        settings_content.terminal.get_or_insert_default().font_size = value;
6306                    },
6307                }),
6308                metadata: None,
6309                files: USER,
6310            }),
6311            SettingsPageItem::SettingItem(SettingItem {
6312                title: "Font Family",
6313                description: "Font family for terminal text. If not set, defaults to buffer font family.",
6314                field: Box::new(SettingField {
6315                    json_path: Some("terminal.font_family"),
6316                    pick: |settings_content| {
6317                        settings_content
6318                            .terminal
6319                            .as_ref()
6320                            .and_then(|terminal| terminal.font_family.as_ref())
6321                            .or(settings_content.theme.buffer_font_family.as_ref())
6322                    },
6323                    write: |settings_content, value| {
6324                        settings_content
6325                            .terminal
6326                            .get_or_insert_default()
6327                            .font_family = value;
6328                    },
6329                }),
6330                metadata: None,
6331                files: USER,
6332            }),
6333            SettingsPageItem::SettingItem(SettingItem {
6334                title: "Font Fallbacks",
6335                description: "Font fallbacks for terminal text. If not set, defaults to buffer font fallbacks.",
6336                field: Box::new(
6337                    SettingField {
6338                        json_path: Some("terminal.font_fallbacks"),
6339                        pick: |settings_content| {
6340                            settings_content
6341                                .terminal
6342                                .as_ref()
6343                                .and_then(|terminal| terminal.font_fallbacks.as_ref())
6344                                .or(settings_content.theme.buffer_font_fallbacks.as_ref())
6345                        },
6346                        write: |settings_content, value| {
6347                            settings_content
6348                                .terminal
6349                                .get_or_insert_default()
6350                                .font_fallbacks = value;
6351                        },
6352                    }
6353                    .unimplemented(),
6354                ),
6355                metadata: None,
6356                files: USER,
6357            }),
6358            SettingsPageItem::SettingItem(SettingItem {
6359                title: "Font Weight",
6360                description: "Font weight for terminal text in CSS weight units (100-900).",
6361                field: Box::new(SettingField {
6362                    json_path: Some("terminal.font_weight"),
6363                    pick: |settings_content| {
6364                        settings_content.terminal.as_ref()?.font_weight.as_ref()
6365                    },
6366                    write: |settings_content, value| {
6367                        settings_content
6368                            .terminal
6369                            .get_or_insert_default()
6370                            .font_weight = value;
6371                    },
6372                }),
6373                metadata: None,
6374                files: USER,
6375            }),
6376            SettingsPageItem::SettingItem(SettingItem {
6377                title: "Font Features",
6378                description: "Font features for terminal text.",
6379                field: Box::new(
6380                    SettingField {
6381                        json_path: Some("terminal.font_features"),
6382                        pick: |settings_content| {
6383                            settings_content
6384                                .terminal
6385                                .as_ref()
6386                                .and_then(|terminal| terminal.font_features.as_ref())
6387                                .or(settings_content.theme.buffer_font_features.as_ref())
6388                        },
6389                        write: |settings_content, value| {
6390                            settings_content
6391                                .terminal
6392                                .get_or_insert_default()
6393                                .font_features = value;
6394                        },
6395                    }
6396                    .unimplemented(),
6397                ),
6398                metadata: None,
6399                files: USER,
6400            }),
6401        ]
6402    }
6403
6404    fn display_settings_section() -> [SettingsPageItem; 6] {
6405        [
6406            SettingsPageItem::SectionHeader("Display Settings"),
6407            SettingsPageItem::SettingItem(SettingItem {
6408                title: "Line Height",
6409                description: "Line height for terminal text.",
6410                field: Box::new(
6411                    SettingField {
6412                        json_path: Some("terminal.line_height"),
6413                        pick: |settings_content| {
6414                            settings_content.terminal.as_ref()?.line_height.as_ref()
6415                        },
6416                        write: |settings_content, value| {
6417                            settings_content
6418                                .terminal
6419                                .get_or_insert_default()
6420                                .line_height = value;
6421                        },
6422                    }
6423                    .unimplemented(),
6424                ),
6425                metadata: None,
6426                files: USER,
6427            }),
6428            SettingsPageItem::SettingItem(SettingItem {
6429                title: "Cursor Shape",
6430                description: "Default cursor shape for the terminal (bar, block, underline, or hollow).",
6431                field: Box::new(SettingField {
6432                    json_path: Some("terminal.cursor_shape"),
6433                    pick: |settings_content| {
6434                        settings_content.terminal.as_ref()?.cursor_shape.as_ref()
6435                    },
6436                    write: |settings_content, value| {
6437                        settings_content
6438                            .terminal
6439                            .get_or_insert_default()
6440                            .cursor_shape = value;
6441                    },
6442                }),
6443                metadata: None,
6444                files: USER,
6445            }),
6446            SettingsPageItem::SettingItem(SettingItem {
6447                title: "Cursor Blinking",
6448                description: "Sets the cursor blinking behavior in the terminal.",
6449                field: Box::new(SettingField {
6450                    json_path: Some("terminal.blinking"),
6451                    pick: |settings_content| settings_content.terminal.as_ref()?.blinking.as_ref(),
6452                    write: |settings_content, value| {
6453                        settings_content.terminal.get_or_insert_default().blinking = value;
6454                    },
6455                }),
6456                metadata: None,
6457                files: USER,
6458            }),
6459            SettingsPageItem::SettingItem(SettingItem {
6460                title: "Alternate Scroll",
6461                description: "Whether alternate scroll mode is active by default (converts mouse scroll to arrow keys in apps like Vim).",
6462                field: Box::new(SettingField {
6463                    json_path: Some("terminal.alternate_scroll"),
6464                    pick: |settings_content| {
6465                        settings_content
6466                            .terminal
6467                            .as_ref()?
6468                            .alternate_scroll
6469                            .as_ref()
6470                    },
6471                    write: |settings_content, value| {
6472                        settings_content
6473                            .terminal
6474                            .get_or_insert_default()
6475                            .alternate_scroll = value;
6476                    },
6477                }),
6478                metadata: None,
6479                files: USER,
6480            }),
6481            SettingsPageItem::SettingItem(SettingItem {
6482                title: "Minimum Contrast",
6483                description: "The minimum APCA perceptual contrast between foreground and background colors (0-106).",
6484                field: Box::new(SettingField {
6485                    json_path: Some("terminal.minimum_contrast"),
6486                    pick: |settings_content| {
6487                        settings_content
6488                            .terminal
6489                            .as_ref()?
6490                            .minimum_contrast
6491                            .as_ref()
6492                    },
6493                    write: |settings_content, value| {
6494                        settings_content
6495                            .terminal
6496                            .get_or_insert_default()
6497                            .minimum_contrast = value;
6498                    },
6499                }),
6500                metadata: None,
6501                files: USER,
6502            }),
6503        ]
6504    }
6505
6506    fn behavior_settings_section() -> [SettingsPageItem; 4] {
6507        [
6508            SettingsPageItem::SectionHeader("Behavior Settings"),
6509            SettingsPageItem::SettingItem(SettingItem {
6510                title: "Option As Meta",
6511                description: "Whether the option key behaves as the meta key.",
6512                field: Box::new(SettingField {
6513                    json_path: Some("terminal.option_as_meta"),
6514                    pick: |settings_content| {
6515                        settings_content.terminal.as_ref()?.option_as_meta.as_ref()
6516                    },
6517                    write: |settings_content, value| {
6518                        settings_content
6519                            .terminal
6520                            .get_or_insert_default()
6521                            .option_as_meta = value;
6522                    },
6523                }),
6524                metadata: None,
6525                files: USER,
6526            }),
6527            SettingsPageItem::SettingItem(SettingItem {
6528                title: "Copy On Select",
6529                description: "Whether selecting text in the terminal automatically copies to the system clipboard.",
6530                field: Box::new(SettingField {
6531                    json_path: Some("terminal.copy_on_select"),
6532                    pick: |settings_content| {
6533                        settings_content.terminal.as_ref()?.copy_on_select.as_ref()
6534                    },
6535                    write: |settings_content, value| {
6536                        settings_content
6537                            .terminal
6538                            .get_or_insert_default()
6539                            .copy_on_select = value;
6540                    },
6541                }),
6542                metadata: None,
6543                files: USER,
6544            }),
6545            SettingsPageItem::SettingItem(SettingItem {
6546                title: "Keep Selection On Copy",
6547                description: "Whether to keep the text selection after copying it to the clipboard.",
6548                field: Box::new(SettingField {
6549                    json_path: Some("terminal.keep_selection_on_copy"),
6550                    pick: |settings_content| {
6551                        settings_content
6552                            .terminal
6553                            .as_ref()?
6554                            .keep_selection_on_copy
6555                            .as_ref()
6556                    },
6557                    write: |settings_content, value| {
6558                        settings_content
6559                            .terminal
6560                            .get_or_insert_default()
6561                            .keep_selection_on_copy = value;
6562                    },
6563                }),
6564                metadata: None,
6565                files: USER,
6566            }),
6567        ]
6568    }
6569
6570    fn layout_settings_section() -> [SettingsPageItem; 3] {
6571        [
6572            SettingsPageItem::SectionHeader("Layout Settings"),
6573            SettingsPageItem::SettingItem(SettingItem {
6574                title: "Default Width",
6575                description: "Default width when the terminal is docked to the left or right (in pixels).",
6576                field: Box::new(SettingField {
6577                    json_path: Some("terminal.default_width"),
6578                    pick: |settings_content| {
6579                        settings_content.terminal.as_ref()?.default_width.as_ref()
6580                    },
6581                    write: |settings_content, value| {
6582                        settings_content
6583                            .terminal
6584                            .get_or_insert_default()
6585                            .default_width = value;
6586                    },
6587                }),
6588                metadata: None,
6589                files: USER,
6590            }),
6591            SettingsPageItem::SettingItem(SettingItem {
6592                title: "Default Height",
6593                description: "Default height when the terminal is docked to the bottom (in pixels).",
6594                field: Box::new(SettingField {
6595                    json_path: Some("terminal.default_height"),
6596                    pick: |settings_content| {
6597                        settings_content.terminal.as_ref()?.default_height.as_ref()
6598                    },
6599                    write: |settings_content, value| {
6600                        settings_content
6601                            .terminal
6602                            .get_or_insert_default()
6603                            .default_height = value;
6604                    },
6605                }),
6606                metadata: None,
6607                files: USER,
6608            }),
6609        ]
6610    }
6611
6612    fn advanced_settings_section() -> [SettingsPageItem; 3] {
6613        [
6614            SettingsPageItem::SectionHeader("Advanced Settings"),
6615            SettingsPageItem::SettingItem(SettingItem {
6616                title: "Max Scroll History Lines",
6617                description: "Maximum number of lines to keep in scrollback history (max: 100,000; 0 disables scrolling).",
6618                field: Box::new(SettingField {
6619                    json_path: Some("terminal.max_scroll_history_lines"),
6620                    pick: |settings_content| {
6621                        settings_content
6622                            .terminal
6623                            .as_ref()?
6624                            .max_scroll_history_lines
6625                            .as_ref()
6626                    },
6627                    write: |settings_content, value| {
6628                        settings_content
6629                            .terminal
6630                            .get_or_insert_default()
6631                            .max_scroll_history_lines = value;
6632                    },
6633                }),
6634                metadata: None,
6635                files: USER,
6636            }),
6637            SettingsPageItem::SettingItem(SettingItem {
6638                title: "Scroll Multiplier",
6639                description: "The multiplier for scrolling in the terminal with the mouse wheel",
6640                field: Box::new(SettingField {
6641                    json_path: Some("terminal.scroll_multiplier"),
6642                    pick: |settings_content| {
6643                        settings_content
6644                            .terminal
6645                            .as_ref()?
6646                            .scroll_multiplier
6647                            .as_ref()
6648                    },
6649                    write: |settings_content, value| {
6650                        settings_content
6651                            .terminal
6652                            .get_or_insert_default()
6653                            .scroll_multiplier = value;
6654                    },
6655                }),
6656                metadata: None,
6657                files: USER,
6658            }),
6659        ]
6660    }
6661
6662    fn toolbar_section() -> [SettingsPageItem; 2] {
6663        [
6664            SettingsPageItem::SectionHeader("Toolbar"),
6665            SettingsPageItem::SettingItem(SettingItem {
6666                title: "Breadcrumbs",
6667                description: "Display the terminal title in breadcrumbs inside the terminal pane.",
6668                field: Box::new(SettingField {
6669                    json_path: Some("terminal.toolbar.breadcrumbs"),
6670                    pick: |settings_content| {
6671                        settings_content
6672                            .terminal
6673                            .as_ref()?
6674                            .toolbar
6675                            .as_ref()?
6676                            .breadcrumbs
6677                            .as_ref()
6678                    },
6679                    write: |settings_content, value| {
6680                        settings_content
6681                            .terminal
6682                            .get_or_insert_default()
6683                            .toolbar
6684                            .get_or_insert_default()
6685                            .breadcrumbs = value;
6686                    },
6687                }),
6688                metadata: None,
6689                files: USER,
6690            }),
6691        ]
6692    }
6693
6694    fn scrollbar_section() -> [SettingsPageItem; 2] {
6695        [
6696            SettingsPageItem::SectionHeader("Scrollbar"),
6697            SettingsPageItem::SettingItem(SettingItem {
6698                title: "Show Scrollbar",
6699                description: "When to show the scrollbar in the terminal.",
6700                field: Box::new(SettingField {
6701                    json_path: Some("terminal.scrollbar.show"),
6702                    pick: |settings_content| {
6703                        show_scrollbar_or_editor(settings_content, |settings_content| {
6704                            settings_content
6705                                .terminal
6706                                .as_ref()?
6707                                .scrollbar
6708                                .as_ref()?
6709                                .show
6710                                .as_ref()
6711                        })
6712                    },
6713                    write: |settings_content, value| {
6714                        settings_content
6715                            .terminal
6716                            .get_or_insert_default()
6717                            .scrollbar
6718                            .get_or_insert_default()
6719                            .show = value;
6720                    },
6721                }),
6722                metadata: None,
6723                files: USER,
6724            }),
6725        ]
6726    }
6727
6728    SettingsPage {
6729        title: "Terminal",
6730        items: concat_sections![
6731            environment_section(),
6732            font_section(),
6733            display_settings_section(),
6734            behavior_settings_section(),
6735            layout_settings_section(),
6736            advanced_settings_section(),
6737            toolbar_section(),
6738            scrollbar_section(),
6739        ],
6740    }
6741}
6742
6743fn version_control_page() -> SettingsPage {
6744    fn git_integration_section() -> [SettingsPageItem; 2] {
6745        [
6746            SettingsPageItem::SectionHeader("Git Integration"),
6747            SettingsPageItem::DynamicItem(DynamicItem {
6748                discriminant: SettingItem {
6749                    files: USER,
6750                    title: "Disable Git Integration",
6751                    description: "Disable all Git integration features in Zed.",
6752                    field: Box::new(SettingField::<bool> {
6753                        json_path: Some("git.disable_git"),
6754                        pick: |settings_content| {
6755                            settings_content
6756                                .git
6757                                .as_ref()?
6758                                .enabled
6759                                .as_ref()?
6760                                .disable_git
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                                .disable_git = value;
6770                        },
6771                    }),
6772                    metadata: None,
6773                },
6774                pick_discriminant: |settings_content| {
6775                    let disabled = settings_content
6776                        .git
6777                        .as_ref()?
6778                        .enabled
6779                        .as_ref()?
6780                        .disable_git
6781                        .unwrap_or(false);
6782                    Some(if disabled { 0 } else { 1 })
6783                },
6784                fields: vec![
6785                    vec![],
6786                    vec![
6787                        SettingItem {
6788                            files: USER,
6789                            title: "Enable Git Status",
6790                            description: "Show Git status information in the editor.",
6791                            field: Box::new(SettingField::<bool> {
6792                                json_path: Some("git.enable_status"),
6793                                pick: |settings_content| {
6794                                    settings_content
6795                                        .git
6796                                        .as_ref()?
6797                                        .enabled
6798                                        .as_ref()?
6799                                        .enable_status
6800                                        .as_ref()
6801                                },
6802                                write: |settings_content, value| {
6803                                    settings_content
6804                                        .git
6805                                        .get_or_insert_default()
6806                                        .enabled
6807                                        .get_or_insert_default()
6808                                        .enable_status = value;
6809                                },
6810                            }),
6811                            metadata: None,
6812                        },
6813                        SettingItem {
6814                            files: USER,
6815                            title: "Enable Git Diff",
6816                            description: "Show Git diff information in the editor.",
6817                            field: Box::new(SettingField::<bool> {
6818                                json_path: Some("git.enable_diff"),
6819                                pick: |settings_content| {
6820                                    settings_content
6821                                        .git
6822                                        .as_ref()?
6823                                        .enabled
6824                                        .as_ref()?
6825                                        .enable_diff
6826                                        .as_ref()
6827                                },
6828                                write: |settings_content, value| {
6829                                    settings_content
6830                                        .git
6831                                        .get_or_insert_default()
6832                                        .enabled
6833                                        .get_or_insert_default()
6834                                        .enable_diff = value;
6835                                },
6836                            }),
6837                            metadata: None,
6838                        },
6839                    ],
6840                ],
6841            }),
6842        ]
6843    }
6844
6845    fn git_gutter_section() -> [SettingsPageItem; 3] {
6846        [
6847            SettingsPageItem::SectionHeader("Git Gutter"),
6848            SettingsPageItem::SettingItem(SettingItem {
6849                title: "Visibility",
6850                description: "Control whether Git status is shown in the editor's gutter.",
6851                field: Box::new(SettingField {
6852                    json_path: Some("git.git_gutter"),
6853                    pick: |settings_content| settings_content.git.as_ref()?.git_gutter.as_ref(),
6854                    write: |settings_content, value| {
6855                        settings_content.git.get_or_insert_default().git_gutter = value;
6856                    },
6857                }),
6858                metadata: None,
6859                files: USER,
6860            }),
6861            // todo(settings_ui): Figure out the right default for this value in default.json
6862            SettingsPageItem::SettingItem(SettingItem {
6863                title: "Debounce",
6864                description: "Debounce threshold in milliseconds after which changes are reflected in the Git gutter.",
6865                field: Box::new(SettingField {
6866                    json_path: Some("git.gutter_debounce"),
6867                    pick: |settings_content| {
6868                        settings_content.git.as_ref()?.gutter_debounce.as_ref()
6869                    },
6870                    write: |settings_content, value| {
6871                        settings_content.git.get_or_insert_default().gutter_debounce = value;
6872                    },
6873                }),
6874                metadata: None,
6875                files: USER,
6876            }),
6877        ]
6878    }
6879
6880    fn inline_git_blame_section() -> [SettingsPageItem; 6] {
6881        [
6882            SettingsPageItem::SectionHeader("Inline Git Blame"),
6883            SettingsPageItem::SettingItem(SettingItem {
6884                title: "Enabled",
6885                description: "Whether or not to show Git blame data inline in the currently focused line.",
6886                field: Box::new(SettingField {
6887                    json_path: Some("git.inline_blame.enabled"),
6888                    pick: |settings_content| {
6889                        settings_content
6890                            .git
6891                            .as_ref()?
6892                            .inline_blame
6893                            .as_ref()?
6894                            .enabled
6895                            .as_ref()
6896                    },
6897                    write: |settings_content, value| {
6898                        settings_content
6899                            .git
6900                            .get_or_insert_default()
6901                            .inline_blame
6902                            .get_or_insert_default()
6903                            .enabled = value;
6904                    },
6905                }),
6906                metadata: None,
6907                files: USER,
6908            }),
6909            SettingsPageItem::SettingItem(SettingItem {
6910                title: "Delay",
6911                description: "The delay after which the inline blame information is shown.",
6912                field: Box::new(SettingField {
6913                    json_path: Some("git.inline_blame.delay_ms"),
6914                    pick: |settings_content| {
6915                        settings_content
6916                            .git
6917                            .as_ref()?
6918                            .inline_blame
6919                            .as_ref()?
6920                            .delay_ms
6921                            .as_ref()
6922                    },
6923                    write: |settings_content, value| {
6924                        settings_content
6925                            .git
6926                            .get_or_insert_default()
6927                            .inline_blame
6928                            .get_or_insert_default()
6929                            .delay_ms = value;
6930                    },
6931                }),
6932                metadata: None,
6933                files: USER,
6934            }),
6935            SettingsPageItem::SettingItem(SettingItem {
6936                title: "Padding",
6937                description: "Padding between the end of the source line and the start of the inline blame in columns.",
6938                field: Box::new(SettingField {
6939                    json_path: Some("git.inline_blame.padding"),
6940                    pick: |settings_content| {
6941                        settings_content
6942                            .git
6943                            .as_ref()?
6944                            .inline_blame
6945                            .as_ref()?
6946                            .padding
6947                            .as_ref()
6948                    },
6949                    write: |settings_content, value| {
6950                        settings_content
6951                            .git
6952                            .get_or_insert_default()
6953                            .inline_blame
6954                            .get_or_insert_default()
6955                            .padding = value;
6956                    },
6957                }),
6958                metadata: None,
6959                files: USER,
6960            }),
6961            SettingsPageItem::SettingItem(SettingItem {
6962                title: "Minimum Column",
6963                description: "The minimum column number at which to show the inline blame information.",
6964                field: Box::new(SettingField {
6965                    json_path: Some("git.inline_blame.min_column"),
6966                    pick: |settings_content| {
6967                        settings_content
6968                            .git
6969                            .as_ref()?
6970                            .inline_blame
6971                            .as_ref()?
6972                            .min_column
6973                            .as_ref()
6974                    },
6975                    write: |settings_content, value| {
6976                        settings_content
6977                            .git
6978                            .get_or_insert_default()
6979                            .inline_blame
6980                            .get_or_insert_default()
6981                            .min_column = value;
6982                    },
6983                }),
6984                metadata: None,
6985                files: USER,
6986            }),
6987            SettingsPageItem::SettingItem(SettingItem {
6988                title: "Show Commit Summary",
6989                description: "Show commit summary as part of the inline blame.",
6990                field: Box::new(SettingField {
6991                    json_path: Some("git.inline_blame.show_commit_summary"),
6992                    pick: |settings_content| {
6993                        settings_content
6994                            .git
6995                            .as_ref()?
6996                            .inline_blame
6997                            .as_ref()?
6998                            .show_commit_summary
6999                            .as_ref()
7000                    },
7001                    write: |settings_content, value| {
7002                        settings_content
7003                            .git
7004                            .get_or_insert_default()
7005                            .inline_blame
7006                            .get_or_insert_default()
7007                            .show_commit_summary = value;
7008                    },
7009                }),
7010                metadata: None,
7011                files: USER,
7012            }),
7013        ]
7014    }
7015
7016    fn git_blame_view_section() -> [SettingsPageItem; 2] {
7017        [
7018            SettingsPageItem::SectionHeader("Git Blame View"),
7019            SettingsPageItem::SettingItem(SettingItem {
7020                title: "Show Avatar",
7021                description: "Show the avatar of the author of the commit.",
7022                field: Box::new(SettingField {
7023                    json_path: Some("git.blame.show_avatar"),
7024                    pick: |settings_content| {
7025                        settings_content
7026                            .git
7027                            .as_ref()?
7028                            .blame
7029                            .as_ref()?
7030                            .show_avatar
7031                            .as_ref()
7032                    },
7033                    write: |settings_content, value| {
7034                        settings_content
7035                            .git
7036                            .get_or_insert_default()
7037                            .blame
7038                            .get_or_insert_default()
7039                            .show_avatar = value;
7040                    },
7041                }),
7042                metadata: None,
7043                files: USER,
7044            }),
7045        ]
7046    }
7047
7048    fn branch_picker_section() -> [SettingsPageItem; 2] {
7049        [
7050            SettingsPageItem::SectionHeader("Branch Picker"),
7051            SettingsPageItem::SettingItem(SettingItem {
7052                title: "Show Author Name",
7053                description: "Show author name as part of the commit information in branch picker.",
7054                field: Box::new(SettingField {
7055                    json_path: Some("git.branch_picker.show_author_name"),
7056                    pick: |settings_content| {
7057                        settings_content
7058                            .git
7059                            .as_ref()?
7060                            .branch_picker
7061                            .as_ref()?
7062                            .show_author_name
7063                            .as_ref()
7064                    },
7065                    write: |settings_content, value| {
7066                        settings_content
7067                            .git
7068                            .get_or_insert_default()
7069                            .branch_picker
7070                            .get_or_insert_default()
7071                            .show_author_name = value;
7072                    },
7073                }),
7074                metadata: None,
7075                files: USER,
7076            }),
7077        ]
7078    }
7079
7080    fn git_hunks_section() -> [SettingsPageItem; 3] {
7081        [
7082            SettingsPageItem::SectionHeader("Git Hunks"),
7083            SettingsPageItem::SettingItem(SettingItem {
7084                title: "Hunk Style",
7085                description: "How Git hunks are displayed visually in the editor.",
7086                field: Box::new(SettingField {
7087                    json_path: Some("git.hunk_style"),
7088                    pick: |settings_content| settings_content.git.as_ref()?.hunk_style.as_ref(),
7089                    write: |settings_content, value| {
7090                        settings_content.git.get_or_insert_default().hunk_style = value;
7091                    },
7092                }),
7093                metadata: None,
7094                files: USER,
7095            }),
7096            SettingsPageItem::SettingItem(SettingItem {
7097                title: "Path Style",
7098                description: "Should the name or path be displayed first in the git view.",
7099                field: Box::new(SettingField {
7100                    json_path: Some("git.path_style"),
7101                    pick: |settings_content| settings_content.git.as_ref()?.path_style.as_ref(),
7102                    write: |settings_content, value| {
7103                        settings_content.git.get_or_insert_default().path_style = value;
7104                    },
7105                }),
7106                metadata: None,
7107                files: USER,
7108            }),
7109        ]
7110    }
7111
7112    SettingsPage {
7113        title: "Version Control",
7114        items: concat_sections![
7115            git_integration_section(),
7116            git_gutter_section(),
7117            inline_git_blame_section(),
7118            git_blame_view_section(),
7119            branch_picker_section(),
7120            git_hunks_section(),
7121        ],
7122    }
7123}
7124
7125fn collaboration_page() -> SettingsPage {
7126    fn calls_section() -> [SettingsPageItem; 3] {
7127        [
7128            SettingsPageItem::SectionHeader("Calls"),
7129            SettingsPageItem::SettingItem(SettingItem {
7130                title: "Mute On Join",
7131                description: "Whether the microphone should be muted when joining a channel or a call.",
7132                field: Box::new(SettingField {
7133                    json_path: Some("calls.mute_on_join"),
7134                    pick: |settings_content| settings_content.calls.as_ref()?.mute_on_join.as_ref(),
7135                    write: |settings_content, value| {
7136                        settings_content.calls.get_or_insert_default().mute_on_join = value;
7137                    },
7138                }),
7139                metadata: None,
7140                files: USER,
7141            }),
7142            SettingsPageItem::SettingItem(SettingItem {
7143                title: "Share On Join",
7144                description: "Whether your current project should be shared when joining an empty channel.",
7145                field: Box::new(SettingField {
7146                    json_path: Some("calls.share_on_join"),
7147                    pick: |settings_content| {
7148                        settings_content.calls.as_ref()?.share_on_join.as_ref()
7149                    },
7150                    write: |settings_content, value| {
7151                        settings_content.calls.get_or_insert_default().share_on_join = value;
7152                    },
7153                }),
7154                metadata: None,
7155                files: USER,
7156            }),
7157        ]
7158    }
7159
7160    fn audio_settings() -> [SettingsPageItem; 3] {
7161        [
7162            SettingsPageItem::ActionLink(ActionLink {
7163                title: "Test Audio".into(),
7164                description: Some("Test your microphone and speaker setup".into()),
7165                button_text: "Test Audio".into(),
7166                on_click: Arc::new(|_settings_window, window, cx| {
7167                    open_audio_test_window(window, cx);
7168                }),
7169                files: USER,
7170            }),
7171            SettingsPageItem::SettingItem(SettingItem {
7172                title: "Output Audio Device",
7173                description: "Select output audio device",
7174                field: Box::new(SettingField {
7175                    json_path: Some("audio.experimental.output_audio_device"),
7176                    pick: |settings_content| {
7177                        settings_content
7178                            .audio
7179                            .as_ref()?
7180                            .output_audio_device
7181                            .as_ref()
7182                            .or(DEFAULT_EMPTY_AUDIO_OUTPUT)
7183                    },
7184                    write: |settings_content, value| {
7185                        settings_content
7186                            .audio
7187                            .get_or_insert_default()
7188                            .output_audio_device = value;
7189                    },
7190                }),
7191                metadata: None,
7192                files: USER,
7193            }),
7194            SettingsPageItem::SettingItem(SettingItem {
7195                title: "Input Audio Device",
7196                description: "Select input audio device",
7197                field: Box::new(SettingField {
7198                    json_path: Some("audio.experimental.input_audio_device"),
7199                    pick: |settings_content| {
7200                        settings_content
7201                            .audio
7202                            .as_ref()?
7203                            .input_audio_device
7204                            .as_ref()
7205                            .or(DEFAULT_EMPTY_AUDIO_INPUT)
7206                    },
7207                    write: |settings_content, value| {
7208                        settings_content
7209                            .audio
7210                            .get_or_insert_default()
7211                            .input_audio_device = value;
7212                    },
7213                }),
7214                metadata: None,
7215                files: USER,
7216            }),
7217        ]
7218    }
7219
7220    SettingsPage {
7221        title: "Collaboration",
7222        items: concat_sections![calls_section(), audio_settings()],
7223    }
7224}
7225
7226fn ai_page(cx: &App) -> SettingsPage {
7227    fn general_section() -> [SettingsPageItem; 2] {
7228        [
7229            SettingsPageItem::SectionHeader("General"),
7230            SettingsPageItem::SettingItem(SettingItem {
7231                title: "Disable AI",
7232                description: "Whether to disable all AI features in Zed.",
7233                field: Box::new(SettingField {
7234                    json_path: Some("disable_ai"),
7235                    pick: |settings_content| settings_content.project.disable_ai.as_ref(),
7236                    write: |settings_content, value| {
7237                        settings_content.project.disable_ai = value;
7238                    },
7239                }),
7240                metadata: None,
7241                files: USER | PROJECT,
7242            }),
7243        ]
7244    }
7245
7246    fn agent_configuration_section(cx: &App) -> Box<[SettingsPageItem]> {
7247        let mut items = vec![
7248            SettingsPageItem::SectionHeader("Agent Configuration"),
7249            SettingsPageItem::SubPageLink(SubPageLink {
7250                title: "Tool Permissions".into(),
7251                r#type: Default::default(),
7252                json_path: Some("agent.tool_permissions"),
7253                description: Some("Set up regex patterns to auto-allow, auto-deny, or always request confirmation, for specific tool inputs.".into()),
7254                in_json: true,
7255                files: USER,
7256                render: render_tool_permissions_setup_page,
7257            }),
7258        ];
7259
7260        if cx.has_flag::<AgentV2FeatureFlag>() {
7261            items.push(SettingsPageItem::SettingItem(SettingItem {
7262                title: "New Thread Location",
7263                description: "Whether to start a new thread in the current local project or in a new Git worktree.",
7264                field: Box::new(SettingField {
7265                    json_path: Some("agent.new_thread_location"),
7266                    pick: |settings_content| {
7267                        settings_content
7268                            .agent
7269                            .as_ref()?
7270                            .new_thread_location
7271                            .as_ref()
7272                    },
7273                    write: |settings_content, value| {
7274                        settings_content
7275                            .agent
7276                            .get_or_insert_default()
7277                            .new_thread_location = value;
7278                    },
7279                }),
7280                metadata: None,
7281                files: USER,
7282            }));
7283        }
7284
7285        items.extend([
7286            SettingsPageItem::SettingItem(SettingItem {
7287                title: "Single File Review",
7288                description: "When enabled, agent edits will also be displayed in single-file buffers for review.",
7289                field: Box::new(SettingField {
7290                    json_path: Some("agent.single_file_review"),
7291                    pick: |settings_content| {
7292                        settings_content.agent.as_ref()?.single_file_review.as_ref()
7293                    },
7294                    write: |settings_content, value| {
7295                        settings_content
7296                            .agent
7297                            .get_or_insert_default()
7298                            .single_file_review = value;
7299                    },
7300                }),
7301                metadata: None,
7302                files: USER,
7303            }),
7304            SettingsPageItem::SettingItem(SettingItem {
7305                title: "Enable Feedback",
7306                description: "Show voting thumbs up/down icon buttons for feedback on agent edits.",
7307                field: Box::new(SettingField {
7308                    json_path: Some("agent.enable_feedback"),
7309                    pick: |settings_content| {
7310                        settings_content.agent.as_ref()?.enable_feedback.as_ref()
7311                    },
7312                    write: |settings_content, value| {
7313                        settings_content
7314                            .agent
7315                            .get_or_insert_default()
7316                            .enable_feedback = value;
7317                    },
7318                }),
7319                metadata: None,
7320                files: USER,
7321            }),
7322            SettingsPageItem::SettingItem(SettingItem {
7323                title: "Notify When Agent Waiting",
7324                description: "Where to show notifications when the agent has completed its response or needs confirmation before running a tool action.",
7325                field: Box::new(SettingField {
7326                    json_path: Some("agent.notify_when_agent_waiting"),
7327                    pick: |settings_content| {
7328                        settings_content
7329                            .agent
7330                            .as_ref()?
7331                            .notify_when_agent_waiting
7332                            .as_ref()
7333                    },
7334                    write: |settings_content, value| {
7335                        settings_content
7336                            .agent
7337                            .get_or_insert_default()
7338                            .notify_when_agent_waiting = value;
7339                    },
7340                }),
7341                metadata: None,
7342                files: USER,
7343            }),
7344            SettingsPageItem::SettingItem(SettingItem {
7345                title: "Play Sound When Agent Done",
7346                description: "When to play a sound when the agent has either completed its response, or needs user input.",
7347                field: Box::new(SettingField {
7348                    json_path: Some("agent.play_sound_when_agent_done"),
7349                    pick: |settings_content| {
7350                        settings_content
7351                            .agent
7352                            .as_ref()?
7353                            .play_sound_when_agent_done
7354                            .as_ref()
7355                    },
7356                    write: |settings_content, value| {
7357                        settings_content
7358                            .agent
7359                            .get_or_insert_default()
7360                            .play_sound_when_agent_done = value;
7361                    },
7362                }),
7363                metadata: None,
7364                files: USER,
7365            }),
7366            SettingsPageItem::SettingItem(SettingItem {
7367                title: "Expand Edit Card",
7368                description: "Whether to have edit cards in the agent panel expanded, showing a Preview of the diff.",
7369                field: Box::new(SettingField {
7370                    json_path: Some("agent.expand_edit_card"),
7371                    pick: |settings_content| {
7372                        settings_content.agent.as_ref()?.expand_edit_card.as_ref()
7373                    },
7374                    write: |settings_content, value| {
7375                        settings_content
7376                            .agent
7377                            .get_or_insert_default()
7378                            .expand_edit_card = value;
7379                    },
7380                }),
7381                metadata: None,
7382                files: USER,
7383            }),
7384            SettingsPageItem::SettingItem(SettingItem {
7385                title: "Expand Terminal Card",
7386                description: "Whether to have terminal cards in the agent panel expanded, showing the whole command output.",
7387                field: Box::new(SettingField {
7388                    json_path: Some("agent.expand_terminal_card"),
7389                    pick: |settings_content| {
7390                        settings_content
7391                            .agent
7392                            .as_ref()?
7393                            .expand_terminal_card
7394                            .as_ref()
7395                    },
7396                    write: |settings_content, value| {
7397                        settings_content
7398                            .agent
7399                            .get_or_insert_default()
7400                            .expand_terminal_card = value;
7401                    },
7402                }),
7403                metadata: None,
7404                files: USER,
7405            }),
7406            SettingsPageItem::SettingItem(SettingItem {
7407                title: "Thinking Display",
7408                description: "How thinking blocks should be displayed by default. 'Auto' fully expands during streaming, then auto-collapses when done. 'Preview' auto-expands with a height constraint during streaming. 'Always Expanded' shows full content. 'Always Collapsed' keeps them collapsed.",
7409                field: Box::new(SettingField {
7410                    json_path: Some("agent.thinking_display"),
7411                    pick: |settings_content| {
7412                        settings_content
7413                            .agent
7414                            .as_ref()?
7415                            .thinking_display
7416                            .as_ref()
7417                    },
7418                    write: |settings_content, value| {
7419                        settings_content
7420                            .agent
7421                            .get_or_insert_default()
7422                            .thinking_display = value;
7423                    },
7424                }),
7425                metadata: None,
7426                files: USER,
7427            }),
7428            SettingsPageItem::SettingItem(SettingItem {
7429                title: "Cancel Generation On Terminal Stop",
7430                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.",
7431                field: Box::new(SettingField {
7432                    json_path: Some("agent.cancel_generation_on_terminal_stop"),
7433                    pick: |settings_content| {
7434                        settings_content
7435                            .agent
7436                            .as_ref()?
7437                            .cancel_generation_on_terminal_stop
7438                            .as_ref()
7439                    },
7440                    write: |settings_content, value| {
7441                        settings_content
7442                            .agent
7443                            .get_or_insert_default()
7444                            .cancel_generation_on_terminal_stop = value;
7445                    },
7446                }),
7447                metadata: None,
7448                files: USER,
7449            }),
7450            SettingsPageItem::SettingItem(SettingItem {
7451                title: "Use Modifier To Send",
7452                description: "Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages.",
7453                field: Box::new(SettingField {
7454                    json_path: Some("agent.use_modifier_to_send"),
7455                    pick: |settings_content| {
7456                        settings_content
7457                            .agent
7458                            .as_ref()?
7459                            .use_modifier_to_send
7460                            .as_ref()
7461                    },
7462                    write: |settings_content, value| {
7463                        settings_content
7464                            .agent
7465                            .get_or_insert_default()
7466                            .use_modifier_to_send = value;
7467                    },
7468                }),
7469                metadata: None,
7470                files: USER,
7471            }),
7472            SettingsPageItem::SettingItem(SettingItem {
7473                title: "Message Editor Min Lines",
7474                description: "Minimum number of lines to display in the agent message editor.",
7475                field: Box::new(SettingField {
7476                    json_path: Some("agent.message_editor_min_lines"),
7477                    pick: |settings_content| {
7478                        settings_content
7479                            .agent
7480                            .as_ref()?
7481                            .message_editor_min_lines
7482                            .as_ref()
7483                    },
7484                    write: |settings_content, value| {
7485                        settings_content
7486                            .agent
7487                            .get_or_insert_default()
7488                            .message_editor_min_lines = value;
7489                    },
7490                }),
7491                metadata: None,
7492                files: USER,
7493            }),
7494            SettingsPageItem::SettingItem(SettingItem {
7495                title: "Show Turn Stats",
7496                description: "Whether to show turn statistics like elapsed time during generation and final turn duration.",
7497                field: Box::new(SettingField {
7498                    json_path: Some("agent.show_turn_stats"),
7499                    pick: |settings_content| {
7500                        settings_content.agent.as_ref()?.show_turn_stats.as_ref()
7501                    },
7502                    write: |settings_content, value| {
7503                        settings_content
7504                            .agent
7505                            .get_or_insert_default()
7506                            .show_turn_stats = value;
7507                    },
7508                }),
7509                metadata: None,
7510                files: USER,
7511            }),
7512            SettingsPageItem::SettingItem(SettingItem {
7513                title: "Show Merge Conflict Indicator",
7514                description: "Whether to show the merge conflict indicator in the status bar that offers to resolve conflicts using the agent.",
7515                field: Box::new(SettingField {
7516                    json_path: Some("agent.show_merge_conflict_indicator"),
7517                    pick: |settings_content| {
7518                        settings_content.agent.as_ref()?.show_merge_conflict_indicator.as_ref()
7519                    },
7520                    write: |settings_content, value| {
7521                        settings_content
7522                            .agent
7523                            .get_or_insert_default()
7524                            .show_merge_conflict_indicator = value;
7525                    },
7526                }),
7527                metadata: None,
7528                files: USER,
7529            }),
7530        ]);
7531
7532        items.into_boxed_slice()
7533    }
7534
7535    fn context_servers_section() -> [SettingsPageItem; 2] {
7536        [
7537            SettingsPageItem::SectionHeader("Context Servers"),
7538            SettingsPageItem::SettingItem(SettingItem {
7539                title: "Context Server Timeout",
7540                description: "Default timeout in seconds for context server tool calls. Can be overridden per-server in context_servers configuration.",
7541                field: Box::new(SettingField {
7542                    json_path: Some("context_server_timeout"),
7543                    pick: |settings_content| {
7544                        settings_content.project.context_server_timeout.as_ref()
7545                    },
7546                    write: |settings_content, value| {
7547                        settings_content.project.context_server_timeout = value;
7548                    },
7549                }),
7550                metadata: None,
7551                files: USER | PROJECT,
7552            }),
7553        ]
7554    }
7555
7556    fn edit_prediction_display_sub_section() -> [SettingsPageItem; 1] {
7557        [SettingsPageItem::SettingItem(SettingItem {
7558            title: "Display Mode",
7559            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.",
7560            field: Box::new(SettingField {
7561                json_path: Some("edit_prediction.display_mode"),
7562                pick: |settings_content| {
7563                    settings_content
7564                        .project
7565                        .all_languages
7566                        .edit_predictions
7567                        .as_ref()?
7568                        .mode
7569                        .as_ref()
7570                },
7571                write: |settings_content, value| {
7572                    settings_content
7573                        .project
7574                        .all_languages
7575                        .edit_predictions
7576                        .get_or_insert_default()
7577                        .mode = value;
7578                },
7579            }),
7580            metadata: None,
7581            files: USER,
7582        })]
7583    }
7584
7585    SettingsPage {
7586        title: "AI",
7587        items: concat_sections![
7588            general_section(),
7589            agent_configuration_section(cx),
7590            context_servers_section(),
7591            edit_prediction_language_settings_section(),
7592            edit_prediction_display_sub_section()
7593        ],
7594    }
7595}
7596
7597fn network_page() -> SettingsPage {
7598    fn network_section() -> [SettingsPageItem; 3] {
7599        [
7600            SettingsPageItem::SectionHeader("Network"),
7601            SettingsPageItem::SettingItem(SettingItem {
7602                title: "Proxy",
7603                description: "The proxy to use for network requests.",
7604                field: Box::new(SettingField {
7605                    json_path: Some("proxy"),
7606                    pick: |settings_content| settings_content.proxy.as_ref(),
7607                    write: |settings_content, value| {
7608                        settings_content.proxy = value;
7609                    },
7610                }),
7611                metadata: Some(Box::new(SettingsFieldMetadata {
7612                    placeholder: Some("socks5h://localhost:10808"),
7613                    ..Default::default()
7614                })),
7615                files: USER,
7616            }),
7617            SettingsPageItem::SettingItem(SettingItem {
7618                title: "Server URL",
7619                description: "The URL of the Zed server to connect to.",
7620                field: Box::new(SettingField {
7621                    json_path: Some("server_url"),
7622                    pick: |settings_content| settings_content.server_url.as_ref(),
7623                    write: |settings_content, value| {
7624                        settings_content.server_url = value;
7625                    },
7626                }),
7627                metadata: Some(Box::new(SettingsFieldMetadata {
7628                    placeholder: Some("https://zed.dev"),
7629                    ..Default::default()
7630                })),
7631                files: USER,
7632            }),
7633        ]
7634    }
7635
7636    SettingsPage {
7637        title: "Network",
7638        items: concat_sections![network_section()],
7639    }
7640}
7641
7642fn language_settings_field<T>(
7643    settings_content: &SettingsContent,
7644    get_language_setting_field: fn(&LanguageSettingsContent) -> Option<&T>,
7645) -> Option<&T> {
7646    let all_languages = &settings_content.project.all_languages;
7647
7648    active_language()
7649        .and_then(|current_language_name| {
7650            all_languages
7651                .languages
7652                .0
7653                .get(current_language_name.as_ref())
7654        })
7655        .and_then(get_language_setting_field)
7656        .or_else(|| get_language_setting_field(&all_languages.defaults))
7657}
7658
7659fn language_settings_field_mut<T>(
7660    settings_content: &mut SettingsContent,
7661    value: Option<T>,
7662    write: fn(&mut LanguageSettingsContent, Option<T>),
7663) {
7664    let all_languages = &mut settings_content.project.all_languages;
7665    let language_content = if let Some(current_language) = active_language() {
7666        all_languages
7667            .languages
7668            .0
7669            .entry(current_language.to_string())
7670            .or_default()
7671    } else {
7672        &mut all_languages.defaults
7673    };
7674    write(language_content, value);
7675}
7676
7677fn language_settings_data() -> Box<[SettingsPageItem]> {
7678    fn indentation_section() -> [SettingsPageItem; 5] {
7679        [
7680            SettingsPageItem::SectionHeader("Indentation"),
7681            SettingsPageItem::SettingItem(SettingItem {
7682                title: "Tab Size",
7683                description: "How many columns a tab should occupy.",
7684                field: Box::new(SettingField {
7685                    json_path: Some("languages.$(language).tab_size"), // TODO(cameron): not JQ syntax because not URL-safe
7686                    pick: |settings_content| {
7687                        language_settings_field(settings_content, |language| {
7688                            language.tab_size.as_ref()
7689                        })
7690                    },
7691                    write: |settings_content, value| {
7692                        language_settings_field_mut(settings_content, value, |language, value| {
7693                            language.tab_size = value;
7694                        })
7695                    },
7696                }),
7697                metadata: None,
7698                files: USER | PROJECT,
7699            }),
7700            SettingsPageItem::SettingItem(SettingItem {
7701                title: "Hard Tabs",
7702                description: "Whether to indent lines using tab characters, as opposed to multiple spaces.",
7703                field: Box::new(SettingField {
7704                    json_path: Some("languages.$(language).hard_tabs"),
7705                    pick: |settings_content| {
7706                        language_settings_field(settings_content, |language| {
7707                            language.hard_tabs.as_ref()
7708                        })
7709                    },
7710                    write: |settings_content, value| {
7711                        language_settings_field_mut(settings_content, value, |language, value| {
7712                            language.hard_tabs = value;
7713                        })
7714                    },
7715                }),
7716                metadata: None,
7717                files: USER | PROJECT,
7718            }),
7719            SettingsPageItem::SettingItem(SettingItem {
7720                title: "Auto Indent",
7721                description: "Controls automatic indentation behavior when typing.",
7722                field: Box::new(SettingField {
7723                    json_path: Some("languages.$(language).auto_indent"),
7724                    pick: |settings_content| {
7725                        language_settings_field(settings_content, |language| {
7726                            language.auto_indent.as_ref()
7727                        })
7728                    },
7729                    write: |settings_content, value| {
7730                        language_settings_field_mut(settings_content, value, |language, value| {
7731                            language.auto_indent = value;
7732                        })
7733                    },
7734                }),
7735                metadata: None,
7736                files: USER | PROJECT,
7737            }),
7738            SettingsPageItem::SettingItem(SettingItem {
7739                title: "Auto Indent On Paste",
7740                description: "Whether indentation of pasted content should be adjusted based on the context.",
7741                field: Box::new(SettingField {
7742                    json_path: Some("languages.$(language).auto_indent_on_paste"),
7743                    pick: |settings_content| {
7744                        language_settings_field(settings_content, |language| {
7745                            language.auto_indent_on_paste.as_ref()
7746                        })
7747                    },
7748                    write: |settings_content, value| {
7749                        language_settings_field_mut(settings_content, value, |language, value| {
7750                            language.auto_indent_on_paste = value;
7751                        })
7752                    },
7753                }),
7754                metadata: None,
7755                files: USER | PROJECT,
7756            }),
7757        ]
7758    }
7759
7760    fn wrapping_section() -> [SettingsPageItem; 6] {
7761        [
7762            SettingsPageItem::SectionHeader("Wrapping"),
7763            SettingsPageItem::SettingItem(SettingItem {
7764                title: "Soft Wrap",
7765                description: "How to soft-wrap long lines of text.",
7766                field: Box::new(SettingField {
7767                    json_path: Some("languages.$(language).soft_wrap"),
7768                    pick: |settings_content| {
7769                        language_settings_field(settings_content, |language| {
7770                            language.soft_wrap.as_ref()
7771                        })
7772                    },
7773                    write: |settings_content, value| {
7774                        language_settings_field_mut(settings_content, value, |language, value| {
7775                            language.soft_wrap = value;
7776                        })
7777                    },
7778                }),
7779                metadata: None,
7780                files: USER | PROJECT,
7781            }),
7782            SettingsPageItem::SettingItem(SettingItem {
7783                title: "Show Wrap Guides",
7784                description: "Show wrap guides in the editor.",
7785                field: Box::new(SettingField {
7786                    json_path: Some("languages.$(language).show_wrap_guides"),
7787                    pick: |settings_content| {
7788                        language_settings_field(settings_content, |language| {
7789                            language.show_wrap_guides.as_ref()
7790                        })
7791                    },
7792                    write: |settings_content, value| {
7793                        language_settings_field_mut(settings_content, value, |language, value| {
7794                            language.show_wrap_guides = value;
7795                        })
7796                    },
7797                }),
7798                metadata: None,
7799                files: USER | PROJECT,
7800            }),
7801            SettingsPageItem::SettingItem(SettingItem {
7802                title: "Preferred Line Length",
7803                description: "The column at which to soft-wrap lines, for buffers where soft-wrap is enabled.",
7804                field: Box::new(SettingField {
7805                    json_path: Some("languages.$(language).preferred_line_length"),
7806                    pick: |settings_content| {
7807                        language_settings_field(settings_content, |language| {
7808                            language.preferred_line_length.as_ref()
7809                        })
7810                    },
7811                    write: |settings_content, value| {
7812                        language_settings_field_mut(settings_content, value, |language, value| {
7813                            language.preferred_line_length = value;
7814                        })
7815                    },
7816                }),
7817                metadata: None,
7818                files: USER | PROJECT,
7819            }),
7820            SettingsPageItem::SettingItem(SettingItem {
7821                title: "Wrap Guides",
7822                description: "Character counts at which to show wrap guides in the editor.",
7823                field: Box::new(
7824                    SettingField {
7825                        json_path: Some("languages.$(language).wrap_guides"),
7826                        pick: |settings_content| {
7827                            language_settings_field(settings_content, |language| {
7828                                language.wrap_guides.as_ref()
7829                            })
7830                        },
7831                        write: |settings_content, value| {
7832                            language_settings_field_mut(
7833                                settings_content,
7834                                value,
7835                                |language, value| {
7836                                    language.wrap_guides = value;
7837                                },
7838                            )
7839                        },
7840                    }
7841                    .unimplemented(),
7842                ),
7843                metadata: None,
7844                files: USER | PROJECT,
7845            }),
7846            SettingsPageItem::SettingItem(SettingItem {
7847                title: "Allow Rewrap",
7848                description: "Controls where the `editor::rewrap` action is allowed for this language.",
7849                field: Box::new(SettingField {
7850                    json_path: Some("languages.$(language).allow_rewrap"),
7851                    pick: |settings_content| {
7852                        language_settings_field(settings_content, |language| {
7853                            language.allow_rewrap.as_ref()
7854                        })
7855                    },
7856                    write: |settings_content, value| {
7857                        language_settings_field_mut(settings_content, value, |language, value| {
7858                            language.allow_rewrap = value;
7859                        })
7860                    },
7861                }),
7862                metadata: None,
7863                files: USER | PROJECT,
7864            }),
7865        ]
7866    }
7867
7868    fn indent_guides_section() -> [SettingsPageItem; 6] {
7869        [
7870            SettingsPageItem::SectionHeader("Indent Guides"),
7871            SettingsPageItem::SettingItem(SettingItem {
7872                title: "Enabled",
7873                description: "Display indent guides in the editor.",
7874                field: Box::new(SettingField {
7875                    json_path: Some("languages.$(language).indent_guides.enabled"),
7876                    pick: |settings_content| {
7877                        language_settings_field(settings_content, |language| {
7878                            language
7879                                .indent_guides
7880                                .as_ref()
7881                                .and_then(|indent_guides| indent_guides.enabled.as_ref())
7882                        })
7883                    },
7884                    write: |settings_content, value| {
7885                        language_settings_field_mut(settings_content, value, |language, value| {
7886                            language.indent_guides.get_or_insert_default().enabled = value;
7887                        })
7888                    },
7889                }),
7890                metadata: None,
7891                files: USER | PROJECT,
7892            }),
7893            SettingsPageItem::SettingItem(SettingItem {
7894                title: "Line Width",
7895                description: "The width of the indent guides in pixels, between 1 and 10.",
7896                field: Box::new(SettingField {
7897                    json_path: Some("languages.$(language).indent_guides.line_width"),
7898                    pick: |settings_content| {
7899                        language_settings_field(settings_content, |language| {
7900                            language
7901                                .indent_guides
7902                                .as_ref()
7903                                .and_then(|indent_guides| indent_guides.line_width.as_ref())
7904                        })
7905                    },
7906                    write: |settings_content, value| {
7907                        language_settings_field_mut(settings_content, value, |language, value| {
7908                            language.indent_guides.get_or_insert_default().line_width = value;
7909                        })
7910                    },
7911                }),
7912                metadata: None,
7913                files: USER | PROJECT,
7914            }),
7915            SettingsPageItem::SettingItem(SettingItem {
7916                title: "Active Line Width",
7917                description: "The width of the active indent guide in pixels, between 1 and 10.",
7918                field: Box::new(SettingField {
7919                    json_path: Some("languages.$(language).indent_guides.active_line_width"),
7920                    pick: |settings_content| {
7921                        language_settings_field(settings_content, |language| {
7922                            language
7923                                .indent_guides
7924                                .as_ref()
7925                                .and_then(|indent_guides| indent_guides.active_line_width.as_ref())
7926                        })
7927                    },
7928                    write: |settings_content, value| {
7929                        language_settings_field_mut(settings_content, value, |language, value| {
7930                            language
7931                                .indent_guides
7932                                .get_or_insert_default()
7933                                .active_line_width = value;
7934                        })
7935                    },
7936                }),
7937                metadata: None,
7938                files: USER | PROJECT,
7939            }),
7940            SettingsPageItem::SettingItem(SettingItem {
7941                title: "Coloring",
7942                description: "Determines how indent guides are colored.",
7943                field: Box::new(SettingField {
7944                    json_path: Some("languages.$(language).indent_guides.coloring"),
7945                    pick: |settings_content| {
7946                        language_settings_field(settings_content, |language| {
7947                            language
7948                                .indent_guides
7949                                .as_ref()
7950                                .and_then(|indent_guides| indent_guides.coloring.as_ref())
7951                        })
7952                    },
7953                    write: |settings_content, value| {
7954                        language_settings_field_mut(settings_content, value, |language, value| {
7955                            language.indent_guides.get_or_insert_default().coloring = value;
7956                        })
7957                    },
7958                }),
7959                metadata: None,
7960                files: USER | PROJECT,
7961            }),
7962            SettingsPageItem::SettingItem(SettingItem {
7963                title: "Background Coloring",
7964                description: "Determines how indent guide backgrounds are colored.",
7965                field: Box::new(SettingField {
7966                    json_path: Some("languages.$(language).indent_guides.background_coloring"),
7967                    pick: |settings_content| {
7968                        language_settings_field(settings_content, |language| {
7969                            language.indent_guides.as_ref().and_then(|indent_guides| {
7970                                indent_guides.background_coloring.as_ref()
7971                            })
7972                        })
7973                    },
7974                    write: |settings_content, value| {
7975                        language_settings_field_mut(settings_content, value, |language, value| {
7976                            language
7977                                .indent_guides
7978                                .get_or_insert_default()
7979                                .background_coloring = value;
7980                        })
7981                    },
7982                }),
7983                metadata: None,
7984                files: USER | PROJECT,
7985            }),
7986        ]
7987    }
7988
7989    fn formatting_section() -> [SettingsPageItem; 7] {
7990        [
7991            SettingsPageItem::SectionHeader("Formatting"),
7992            SettingsPageItem::SettingItem(SettingItem {
7993                title: "Format On Save",
7994                description: "Whether or not to perform a buffer format before saving.",
7995                field: Box::new(
7996                    // TODO(settings_ui): this setting should just be a bool
7997                    SettingField {
7998                        json_path: Some("languages.$(language).format_on_save"),
7999                        pick: |settings_content| {
8000                            language_settings_field(settings_content, |language| {
8001                                language.format_on_save.as_ref()
8002                            })
8003                        },
8004                        write: |settings_content, value| {
8005                            language_settings_field_mut(
8006                                settings_content,
8007                                value,
8008                                |language, value| {
8009                                    language.format_on_save = value;
8010                                },
8011                            )
8012                        },
8013                    },
8014                ),
8015                metadata: None,
8016                files: USER | PROJECT,
8017            }),
8018            SettingsPageItem::SettingItem(SettingItem {
8019                title: "Remove Trailing Whitespace On Save",
8020                description: "Whether or not to remove any trailing whitespace from lines of a buffer before saving it.",
8021                field: Box::new(SettingField {
8022                    json_path: Some("languages.$(language).remove_trailing_whitespace_on_save"),
8023                    pick: |settings_content| {
8024                        language_settings_field(settings_content, |language| {
8025                            language.remove_trailing_whitespace_on_save.as_ref()
8026                        })
8027                    },
8028                    write: |settings_content, value| {
8029                        language_settings_field_mut(settings_content, value, |language, value| {
8030                            language.remove_trailing_whitespace_on_save = value;
8031                        })
8032                    },
8033                }),
8034                metadata: None,
8035                files: USER | PROJECT,
8036            }),
8037            SettingsPageItem::SettingItem(SettingItem {
8038                title: "Ensure Final Newline On Save",
8039                description: "Whether or not to ensure there's a single newline at the end of a buffer when saving it.",
8040                field: Box::new(SettingField {
8041                    json_path: Some("languages.$(language).ensure_final_newline_on_save"),
8042                    pick: |settings_content| {
8043                        language_settings_field(settings_content, |language| {
8044                            language.ensure_final_newline_on_save.as_ref()
8045                        })
8046                    },
8047                    write: |settings_content, value| {
8048                        language_settings_field_mut(settings_content, value, |language, value| {
8049                            language.ensure_final_newline_on_save = value;
8050                        })
8051                    },
8052                }),
8053                metadata: None,
8054                files: USER | PROJECT,
8055            }),
8056            SettingsPageItem::SettingItem(SettingItem {
8057                title: "Formatter",
8058                description: "How to perform a buffer format.",
8059                field: Box::new(
8060                    SettingField {
8061                        json_path: Some("languages.$(language).formatter"),
8062                        pick: |settings_content| {
8063                            language_settings_field(settings_content, |language| {
8064                                language.formatter.as_ref()
8065                            })
8066                        },
8067                        write: |settings_content, value| {
8068                            language_settings_field_mut(
8069                                settings_content,
8070                                value,
8071                                |language, value| {
8072                                    language.formatter = value;
8073                                },
8074                            )
8075                        },
8076                    }
8077                    .unimplemented(),
8078                ),
8079                metadata: None,
8080                files: USER | PROJECT,
8081            }),
8082            SettingsPageItem::SettingItem(SettingItem {
8083                title: "Use On Type Format",
8084                description: "Whether to use additional LSP queries to format (and amend) the code after every \"trigger\" symbol input, defined by LSP server capabilities",
8085                field: Box::new(SettingField {
8086                    json_path: Some("languages.$(language).use_on_type_format"),
8087                    pick: |settings_content| {
8088                        language_settings_field(settings_content, |language| {
8089                            language.use_on_type_format.as_ref()
8090                        })
8091                    },
8092                    write: |settings_content, value| {
8093                        language_settings_field_mut(settings_content, value, |language, value| {
8094                            language.use_on_type_format = value;
8095                        })
8096                    },
8097                }),
8098                metadata: None,
8099                files: USER | PROJECT,
8100            }),
8101            SettingsPageItem::SettingItem(SettingItem {
8102                title: "Code Actions On Format",
8103                description: "Additional code actions to run when formatting.",
8104                field: Box::new(
8105                    SettingField {
8106                        json_path: Some("languages.$(language).code_actions_on_format"),
8107                        pick: |settings_content| {
8108                            language_settings_field(settings_content, |language| {
8109                                language.code_actions_on_format.as_ref()
8110                            })
8111                        },
8112                        write: |settings_content, value| {
8113                            language_settings_field_mut(
8114                                settings_content,
8115                                value,
8116                                |language, value| {
8117                                    language.code_actions_on_format = value;
8118                                },
8119                            )
8120                        },
8121                    }
8122                    .unimplemented(),
8123                ),
8124                metadata: None,
8125                files: USER | PROJECT,
8126            }),
8127        ]
8128    }
8129
8130    fn autoclose_section() -> [SettingsPageItem; 5] {
8131        [
8132            SettingsPageItem::SectionHeader("Autoclose"),
8133            SettingsPageItem::SettingItem(SettingItem {
8134                title: "Use Autoclose",
8135                description: "Whether to automatically type closing characters for you. For example, when you type '(', Zed will automatically add a closing ')' at the correct position.",
8136                field: Box::new(SettingField {
8137                    json_path: Some("languages.$(language).use_autoclose"),
8138                    pick: |settings_content| {
8139                        language_settings_field(settings_content, |language| {
8140                            language.use_autoclose.as_ref()
8141                        })
8142                    },
8143                    write: |settings_content, value| {
8144                        language_settings_field_mut(settings_content, value, |language, value| {
8145                            language.use_autoclose = value;
8146                        })
8147                    },
8148                }),
8149                metadata: None,
8150                files: USER | PROJECT,
8151            }),
8152            SettingsPageItem::SettingItem(SettingItem {
8153                title: "Use Auto Surround",
8154                description: "Whether to automatically surround text with characters for you. For example, when you select text and type '(', Zed will automatically surround text with ().",
8155                field: Box::new(SettingField {
8156                    json_path: Some("languages.$(language).use_auto_surround"),
8157                    pick: |settings_content| {
8158                        language_settings_field(settings_content, |language| {
8159                            language.use_auto_surround.as_ref()
8160                        })
8161                    },
8162                    write: |settings_content, value| {
8163                        language_settings_field_mut(settings_content, value, |language, value| {
8164                            language.use_auto_surround = value;
8165                        })
8166                    },
8167                }),
8168                metadata: None,
8169                files: USER | PROJECT,
8170            }),
8171            SettingsPageItem::SettingItem(SettingItem {
8172                title: "Always Treat Brackets As Autoclosed",
8173                description: "Controls whether the closing characters are always skipped over and auto-removed no matter how they were inserted.",
8174                field: Box::new(SettingField {
8175                    json_path: Some("languages.$(language).always_treat_brackets_as_autoclosed"),
8176                    pick: |settings_content| {
8177                        language_settings_field(settings_content, |language| {
8178                            language.always_treat_brackets_as_autoclosed.as_ref()
8179                        })
8180                    },
8181                    write: |settings_content, value| {
8182                        language_settings_field_mut(settings_content, value, |language, value| {
8183                            language.always_treat_brackets_as_autoclosed = value;
8184                        })
8185                    },
8186                }),
8187                metadata: None,
8188                files: USER | PROJECT,
8189            }),
8190            SettingsPageItem::SettingItem(SettingItem {
8191                title: "JSX Tag Auto Close",
8192                description: "Whether to automatically close JSX tags.",
8193                field: Box::new(SettingField {
8194                    json_path: Some("languages.$(language).jsx_tag_auto_close"),
8195                    // TODO(settings_ui): this setting should just be a bool
8196                    pick: |settings_content| {
8197                        language_settings_field(settings_content, |language| {
8198                            language.jsx_tag_auto_close.as_ref()?.enabled.as_ref()
8199                        })
8200                    },
8201                    write: |settings_content, value| {
8202                        language_settings_field_mut(settings_content, value, |language, value| {
8203                            language.jsx_tag_auto_close.get_or_insert_default().enabled = value;
8204                        })
8205                    },
8206                }),
8207                metadata: None,
8208                files: USER | PROJECT,
8209            }),
8210        ]
8211    }
8212
8213    fn whitespace_section() -> [SettingsPageItem; 4] {
8214        [
8215            SettingsPageItem::SectionHeader("Whitespace"),
8216            SettingsPageItem::SettingItem(SettingItem {
8217                title: "Show Whitespaces",
8218                description: "Whether to show tabs and spaces in the editor.",
8219                field: Box::new(SettingField {
8220                    json_path: Some("languages.$(language).show_whitespaces"),
8221                    pick: |settings_content| {
8222                        language_settings_field(settings_content, |language| {
8223                            language.show_whitespaces.as_ref()
8224                        })
8225                    },
8226                    write: |settings_content, value| {
8227                        language_settings_field_mut(settings_content, value, |language, value| {
8228                            language.show_whitespaces = value;
8229                        })
8230                    },
8231                }),
8232                metadata: None,
8233                files: USER | PROJECT,
8234            }),
8235            SettingsPageItem::SettingItem(SettingItem {
8236                title: "Space Whitespace Indicator",
8237                description: "Visible character used to render space characters when show_whitespaces is enabled (default: \"\")",
8238                field: Box::new(
8239                    SettingField {
8240                        json_path: Some("languages.$(language).whitespace_map.space"),
8241                        pick: |settings_content| {
8242                            language_settings_field(settings_content, |language| {
8243                                language.whitespace_map.as_ref()?.space.as_ref()
8244                            })
8245                        },
8246                        write: |settings_content, value| {
8247                            language_settings_field_mut(
8248                                settings_content,
8249                                value,
8250                                |language, value| {
8251                                    language.whitespace_map.get_or_insert_default().space = value;
8252                                },
8253                            )
8254                        },
8255                    }
8256                    .unimplemented(),
8257                ),
8258                metadata: None,
8259                files: USER | PROJECT,
8260            }),
8261            SettingsPageItem::SettingItem(SettingItem {
8262                title: "Tab Whitespace Indicator",
8263                description: "Visible character used to render tab characters when show_whitespaces is enabled (default: \"\")",
8264                field: Box::new(
8265                    SettingField {
8266                        json_path: Some("languages.$(language).whitespace_map.tab"),
8267                        pick: |settings_content| {
8268                            language_settings_field(settings_content, |language| {
8269                                language.whitespace_map.as_ref()?.tab.as_ref()
8270                            })
8271                        },
8272                        write: |settings_content, value| {
8273                            language_settings_field_mut(
8274                                settings_content,
8275                                value,
8276                                |language, value| {
8277                                    language.whitespace_map.get_or_insert_default().tab = value;
8278                                },
8279                            )
8280                        },
8281                    }
8282                    .unimplemented(),
8283                ),
8284                metadata: None,
8285                files: USER | PROJECT,
8286            }),
8287        ]
8288    }
8289
8290    fn completions_section() -> [SettingsPageItem; 7] {
8291        [
8292            SettingsPageItem::SectionHeader("Completions"),
8293            SettingsPageItem::SettingItem(SettingItem {
8294                title: "Show Completions On Input",
8295                description: "Whether to pop the completions menu while typing in an editor without explicitly requesting it.",
8296                field: Box::new(SettingField {
8297                    json_path: Some("languages.$(language).show_completions_on_input"),
8298                    pick: |settings_content| {
8299                        language_settings_field(settings_content, |language| {
8300                            language.show_completions_on_input.as_ref()
8301                        })
8302                    },
8303                    write: |settings_content, value| {
8304                        language_settings_field_mut(settings_content, value, |language, value| {
8305                            language.show_completions_on_input = value;
8306                        })
8307                    },
8308                }),
8309                metadata: None,
8310                files: USER | PROJECT,
8311            }),
8312            SettingsPageItem::SettingItem(SettingItem {
8313                title: "Show Completion Documentation",
8314                description: "Whether to display inline and alongside documentation for items in the completions menu.",
8315                field: Box::new(SettingField {
8316                    json_path: Some("languages.$(language).show_completion_documentation"),
8317                    pick: |settings_content| {
8318                        language_settings_field(settings_content, |language| {
8319                            language.show_completion_documentation.as_ref()
8320                        })
8321                    },
8322                    write: |settings_content, value| {
8323                        language_settings_field_mut(settings_content, value, |language, value| {
8324                            language.show_completion_documentation = value;
8325                        })
8326                    },
8327                }),
8328                metadata: None,
8329                files: USER | PROJECT,
8330            }),
8331            SettingsPageItem::SettingItem(SettingItem {
8332                title: "Words",
8333                description: "Controls how words are completed.",
8334                field: Box::new(SettingField {
8335                    json_path: Some("languages.$(language).completions.words"),
8336                    pick: |settings_content| {
8337                        language_settings_field(settings_content, |language| {
8338                            language.completions.as_ref()?.words.as_ref()
8339                        })
8340                    },
8341                    write: |settings_content, value| {
8342                        language_settings_field_mut(settings_content, value, |language, value| {
8343                            language.completions.get_or_insert_default().words = value;
8344                        })
8345                    },
8346                }),
8347                metadata: None,
8348                files: USER | PROJECT,
8349            }),
8350            SettingsPageItem::SettingItem(SettingItem {
8351                title: "Words Min Length",
8352                description: "How many characters has to be in the completions query to automatically show the words-based completions.",
8353                field: Box::new(SettingField {
8354                    json_path: Some("languages.$(language).completions.words_min_length"),
8355                    pick: |settings_content| {
8356                        language_settings_field(settings_content, |language| {
8357                            language.completions.as_ref()?.words_min_length.as_ref()
8358                        })
8359                    },
8360                    write: |settings_content, value| {
8361                        language_settings_field_mut(settings_content, value, |language, value| {
8362                            language
8363                                .completions
8364                                .get_or_insert_default()
8365                                .words_min_length = value;
8366                        })
8367                    },
8368                }),
8369                metadata: None,
8370                files: USER | PROJECT,
8371            }),
8372            SettingsPageItem::SettingItem(SettingItem {
8373                title: "Completion Menu Scrollbar",
8374                description: "When to show the scrollbar in the completion menu.",
8375                field: Box::new(SettingField {
8376                    json_path: Some("editor.completion_menu_scrollbar"),
8377                    pick: |settings_content| {
8378                        settings_content.editor.completion_menu_scrollbar.as_ref()
8379                    },
8380                    write: |settings_content, value| {
8381                        settings_content.editor.completion_menu_scrollbar = value;
8382                    },
8383                }),
8384                metadata: None,
8385                files: USER,
8386            }),
8387            SettingsPageItem::SettingItem(SettingItem {
8388                title: "Completion Detail Alignment",
8389                description: "Whether to align detail text in code completions context menus left or right.",
8390                field: Box::new(SettingField {
8391                    json_path: Some("editor.completion_detail_alignment"),
8392                    pick: |settings_content| {
8393                        settings_content.editor.completion_detail_alignment.as_ref()
8394                    },
8395                    write: |settings_content, value| {
8396                        settings_content.editor.completion_detail_alignment = value;
8397                    },
8398                }),
8399                metadata: None,
8400                files: USER,
8401            }),
8402        ]
8403    }
8404
8405    fn inlay_hints_section() -> [SettingsPageItem; 10] {
8406        [
8407            SettingsPageItem::SectionHeader("Inlay Hints"),
8408            SettingsPageItem::SettingItem(SettingItem {
8409                title: "Enabled",
8410                description: "Global switch to toggle hints on and off.",
8411                field: Box::new(SettingField {
8412                    json_path: Some("languages.$(language).inlay_hints.enabled"),
8413                    pick: |settings_content| {
8414                        language_settings_field(settings_content, |language| {
8415                            language.inlay_hints.as_ref()?.enabled.as_ref()
8416                        })
8417                    },
8418                    write: |settings_content, value| {
8419                        language_settings_field_mut(settings_content, value, |language, value| {
8420                            language.inlay_hints.get_or_insert_default().enabled = value;
8421                        })
8422                    },
8423                }),
8424                metadata: None,
8425                files: USER | PROJECT,
8426            }),
8427            SettingsPageItem::SettingItem(SettingItem {
8428                title: "Show Value Hints",
8429                description: "Global switch to toggle inline values on and off when debugging.",
8430                field: Box::new(SettingField {
8431                    json_path: Some("languages.$(language).inlay_hints.show_value_hints"),
8432                    pick: |settings_content| {
8433                        language_settings_field(settings_content, |language| {
8434                            language.inlay_hints.as_ref()?.show_value_hints.as_ref()
8435                        })
8436                    },
8437                    write: |settings_content, value| {
8438                        language_settings_field_mut(settings_content, value, |language, value| {
8439                            language
8440                                .inlay_hints
8441                                .get_or_insert_default()
8442                                .show_value_hints = value;
8443                        })
8444                    },
8445                }),
8446                metadata: None,
8447                files: USER | PROJECT,
8448            }),
8449            SettingsPageItem::SettingItem(SettingItem {
8450                title: "Show Type Hints",
8451                description: "Whether type hints should be shown.",
8452                field: Box::new(SettingField {
8453                    json_path: Some("languages.$(language).inlay_hints.show_type_hints"),
8454                    pick: |settings_content| {
8455                        language_settings_field(settings_content, |language| {
8456                            language.inlay_hints.as_ref()?.show_type_hints.as_ref()
8457                        })
8458                    },
8459                    write: |settings_content, value| {
8460                        language_settings_field_mut(settings_content, value, |language, value| {
8461                            language.inlay_hints.get_or_insert_default().show_type_hints = value;
8462                        })
8463                    },
8464                }),
8465                metadata: None,
8466                files: USER | PROJECT,
8467            }),
8468            SettingsPageItem::SettingItem(SettingItem {
8469                title: "Show Parameter Hints",
8470                description: "Whether parameter hints should be shown.",
8471                field: Box::new(SettingField {
8472                    json_path: Some("languages.$(language).inlay_hints.show_parameter_hints"),
8473                    pick: |settings_content| {
8474                        language_settings_field(settings_content, |language| {
8475                            language.inlay_hints.as_ref()?.show_parameter_hints.as_ref()
8476                        })
8477                    },
8478                    write: |settings_content, value| {
8479                        language_settings_field_mut(settings_content, value, |language, value| {
8480                            language
8481                                .inlay_hints
8482                                .get_or_insert_default()
8483                                .show_parameter_hints = value;
8484                        })
8485                    },
8486                }),
8487                metadata: None,
8488                files: USER | PROJECT,
8489            }),
8490            SettingsPageItem::SettingItem(SettingItem {
8491                title: "Show Other Hints",
8492                description: "Whether other hints should be shown.",
8493                field: Box::new(SettingField {
8494                    json_path: Some("languages.$(language).inlay_hints.show_other_hints"),
8495                    pick: |settings_content| {
8496                        language_settings_field(settings_content, |language| {
8497                            language.inlay_hints.as_ref()?.show_other_hints.as_ref()
8498                        })
8499                    },
8500                    write: |settings_content, value| {
8501                        language_settings_field_mut(settings_content, value, |language, value| {
8502                            language
8503                                .inlay_hints
8504                                .get_or_insert_default()
8505                                .show_other_hints = value;
8506                        })
8507                    },
8508                }),
8509                metadata: None,
8510                files: USER | PROJECT,
8511            }),
8512            SettingsPageItem::SettingItem(SettingItem {
8513                title: "Show Background",
8514                description: "Show a background for inlay hints.",
8515                field: Box::new(SettingField {
8516                    json_path: Some("languages.$(language).inlay_hints.show_background"),
8517                    pick: |settings_content| {
8518                        language_settings_field(settings_content, |language| {
8519                            language.inlay_hints.as_ref()?.show_background.as_ref()
8520                        })
8521                    },
8522                    write: |settings_content, value| {
8523                        language_settings_field_mut(settings_content, value, |language, value| {
8524                            language.inlay_hints.get_or_insert_default().show_background = value;
8525                        })
8526                    },
8527                }),
8528                metadata: None,
8529                files: USER | PROJECT,
8530            }),
8531            SettingsPageItem::SettingItem(SettingItem {
8532                title: "Edit Debounce Ms",
8533                description: "Whether or not to debounce inlay hints updates after buffer edits (set to 0 to disable debouncing).",
8534                field: Box::new(SettingField {
8535                    json_path: Some("languages.$(language).inlay_hints.edit_debounce_ms"),
8536                    pick: |settings_content| {
8537                        language_settings_field(settings_content, |language| {
8538                            language.inlay_hints.as_ref()?.edit_debounce_ms.as_ref()
8539                        })
8540                    },
8541                    write: |settings_content, value| {
8542                        language_settings_field_mut(settings_content, value, |language, value| {
8543                            language
8544                                .inlay_hints
8545                                .get_or_insert_default()
8546                                .edit_debounce_ms = value;
8547                        })
8548                    },
8549                }),
8550                metadata: None,
8551                files: USER | PROJECT,
8552            }),
8553            SettingsPageItem::SettingItem(SettingItem {
8554                title: "Scroll Debounce Ms",
8555                description: "Whether or not to debounce inlay hints updates after buffer scrolls (set to 0 to disable debouncing).",
8556                field: Box::new(SettingField {
8557                    json_path: Some("languages.$(language).inlay_hints.scroll_debounce_ms"),
8558                    pick: |settings_content| {
8559                        language_settings_field(settings_content, |language| {
8560                            language.inlay_hints.as_ref()?.scroll_debounce_ms.as_ref()
8561                        })
8562                    },
8563                    write: |settings_content, value| {
8564                        language_settings_field_mut(settings_content, value, |language, value| {
8565                            language
8566                                .inlay_hints
8567                                .get_or_insert_default()
8568                                .scroll_debounce_ms = value;
8569                        })
8570                    },
8571                }),
8572                metadata: None,
8573                files: USER | PROJECT,
8574            }),
8575            SettingsPageItem::SettingItem(SettingItem {
8576                title: "Toggle On Modifiers Press",
8577                description: "Toggles inlay hints (hides or shows) when the user presses the modifiers specified.",
8578                field: Box::new(
8579                    SettingField {
8580                        json_path: Some(
8581                            "languages.$(language).inlay_hints.toggle_on_modifiers_press",
8582                        ),
8583                        pick: |settings_content| {
8584                            language_settings_field(settings_content, |language| {
8585                                language
8586                                    .inlay_hints
8587                                    .as_ref()?
8588                                    .toggle_on_modifiers_press
8589                                    .as_ref()
8590                            })
8591                        },
8592                        write: |settings_content, value| {
8593                            language_settings_field_mut(
8594                                settings_content,
8595                                value,
8596                                |language, value| {
8597                                    language
8598                                        .inlay_hints
8599                                        .get_or_insert_default()
8600                                        .toggle_on_modifiers_press = value;
8601                                },
8602                            )
8603                        },
8604                    }
8605                    .unimplemented(),
8606                ),
8607                metadata: None,
8608                files: USER | PROJECT,
8609            }),
8610        ]
8611    }
8612
8613    fn tasks_section() -> [SettingsPageItem; 4] {
8614        [
8615            SettingsPageItem::SectionHeader("Tasks"),
8616            SettingsPageItem::SettingItem(SettingItem {
8617                title: "Enabled",
8618                description: "Whether tasks are enabled for this language.",
8619                field: Box::new(SettingField {
8620                    json_path: Some("languages.$(language).tasks.enabled"),
8621                    pick: |settings_content| {
8622                        language_settings_field(settings_content, |language| {
8623                            language.tasks.as_ref()?.enabled.as_ref()
8624                        })
8625                    },
8626                    write: |settings_content, value| {
8627                        language_settings_field_mut(settings_content, value, |language, value| {
8628                            language.tasks.get_or_insert_default().enabled = value;
8629                        })
8630                    },
8631                }),
8632                metadata: None,
8633                files: USER | PROJECT,
8634            }),
8635            SettingsPageItem::SettingItem(SettingItem {
8636                title: "Variables",
8637                description: "Extra task variables to set for a particular language.",
8638                field: Box::new(
8639                    SettingField {
8640                        json_path: Some("languages.$(language).tasks.variables"),
8641                        pick: |settings_content| {
8642                            language_settings_field(settings_content, |language| {
8643                                language.tasks.as_ref()?.variables.as_ref()
8644                            })
8645                        },
8646                        write: |settings_content, value| {
8647                            language_settings_field_mut(
8648                                settings_content,
8649                                value,
8650                                |language, value| {
8651                                    language.tasks.get_or_insert_default().variables = value;
8652                                },
8653                            )
8654                        },
8655                    }
8656                    .unimplemented(),
8657                ),
8658                metadata: None,
8659                files: USER | PROJECT,
8660            }),
8661            SettingsPageItem::SettingItem(SettingItem {
8662                title: "Prefer LSP",
8663                description: "Use LSP tasks over Zed language extension tasks.",
8664                field: Box::new(SettingField {
8665                    json_path: Some("languages.$(language).tasks.prefer_lsp"),
8666                    pick: |settings_content| {
8667                        language_settings_field(settings_content, |language| {
8668                            language.tasks.as_ref()?.prefer_lsp.as_ref()
8669                        })
8670                    },
8671                    write: |settings_content, value| {
8672                        language_settings_field_mut(settings_content, value, |language, value| {
8673                            language.tasks.get_or_insert_default().prefer_lsp = value;
8674                        })
8675                    },
8676                }),
8677                metadata: None,
8678                files: USER | PROJECT,
8679            }),
8680        ]
8681    }
8682
8683    fn miscellaneous_section() -> [SettingsPageItem; 7] {
8684        [
8685            SettingsPageItem::SectionHeader("Miscellaneous"),
8686            SettingsPageItem::SettingItem(SettingItem {
8687                title: "Word Diff Enabled",
8688                description: "Whether to enable word diff highlighting in the editor. When enabled, changed words within modified lines are highlighted to show exactly what changed.",
8689                field: Box::new(SettingField {
8690                    json_path: Some("languages.$(language).word_diff_enabled"),
8691                    pick: |settings_content| {
8692                        language_settings_field(settings_content, |language| {
8693                            language.word_diff_enabled.as_ref()
8694                        })
8695                    },
8696                    write: |settings_content, value| {
8697                        language_settings_field_mut(settings_content, value, |language, value| {
8698                            language.word_diff_enabled = value;
8699                        })
8700                    },
8701                }),
8702                metadata: None,
8703                files: USER | PROJECT,
8704            }),
8705            SettingsPageItem::SettingItem(SettingItem {
8706                title: "Debuggers",
8707                description: "Preferred debuggers for this language.",
8708                field: Box::new(
8709                    SettingField {
8710                        json_path: Some("languages.$(language).debuggers"),
8711                        pick: |settings_content| {
8712                            language_settings_field(settings_content, |language| {
8713                                language.debuggers.as_ref()
8714                            })
8715                        },
8716                        write: |settings_content, value| {
8717                            language_settings_field_mut(
8718                                settings_content,
8719                                value,
8720                                |language, value| {
8721                                    language.debuggers = value;
8722                                },
8723                            )
8724                        },
8725                    }
8726                    .unimplemented(),
8727                ),
8728                metadata: None,
8729                files: USER | PROJECT,
8730            }),
8731            SettingsPageItem::SettingItem(SettingItem {
8732                title: "Middle Click Paste",
8733                description: "Enable middle-click paste on Linux.",
8734                field: Box::new(SettingField {
8735                    json_path: Some("languages.$(language).editor.middle_click_paste"),
8736                    pick: |settings_content| settings_content.editor.middle_click_paste.as_ref(),
8737                    write: |settings_content, value| {
8738                        settings_content.editor.middle_click_paste = value;
8739                    },
8740                }),
8741                metadata: None,
8742                files: USER,
8743            }),
8744            SettingsPageItem::SettingItem(SettingItem {
8745                title: "Extend Comment On Newline",
8746                description: "Whether to start a new line with a comment when a previous line is a comment as well.",
8747                field: Box::new(SettingField {
8748                    json_path: Some("languages.$(language).extend_comment_on_newline"),
8749                    pick: |settings_content| {
8750                        language_settings_field(settings_content, |language| {
8751                            language.extend_comment_on_newline.as_ref()
8752                        })
8753                    },
8754                    write: |settings_content, value| {
8755                        language_settings_field_mut(settings_content, value, |language, value| {
8756                            language.extend_comment_on_newline = value;
8757                        })
8758                    },
8759                }),
8760                metadata: None,
8761                files: USER | PROJECT,
8762            }),
8763            SettingsPageItem::SettingItem(SettingItem {
8764                title: "Colorize Brackets",
8765                description: "Whether to colorize brackets in the editor.",
8766                field: Box::new(SettingField {
8767                    json_path: Some("languages.$(language).colorize_brackets"),
8768                    pick: |settings_content| {
8769                        language_settings_field(settings_content, |language| {
8770                            language.colorize_brackets.as_ref()
8771                        })
8772                    },
8773                    write: |settings_content, value| {
8774                        language_settings_field_mut(settings_content, value, |language, value| {
8775                            language.colorize_brackets = value;
8776                        })
8777                    },
8778                }),
8779                metadata: None,
8780                files: USER | PROJECT,
8781            }),
8782            SettingsPageItem::SettingItem(SettingItem {
8783                title: "Vim/Emacs Modeline Support",
8784                description: "Number of lines to search for modelines (set to 0 to disable).",
8785                field: Box::new(SettingField {
8786                    json_path: Some("modeline_lines"),
8787                    pick: |settings_content| settings_content.modeline_lines.as_ref(),
8788                    write: |settings_content, value| {
8789                        settings_content.modeline_lines = value;
8790                    },
8791                }),
8792                metadata: None,
8793                files: USER | PROJECT,
8794            }),
8795        ]
8796    }
8797
8798    fn global_only_miscellaneous_sub_section() -> [SettingsPageItem; 3] {
8799        [
8800            SettingsPageItem::SettingItem(SettingItem {
8801                title: "Image Viewer",
8802                description: "The unit for image file sizes.",
8803                field: Box::new(SettingField {
8804                    json_path: Some("image_viewer.unit"),
8805                    pick: |settings_content| {
8806                        settings_content
8807                            .image_viewer
8808                            .as_ref()
8809                            .and_then(|image_viewer| image_viewer.unit.as_ref())
8810                    },
8811                    write: |settings_content, value| {
8812                        settings_content.image_viewer.get_or_insert_default().unit = value;
8813                    },
8814                }),
8815                metadata: None,
8816                files: USER,
8817            }),
8818            SettingsPageItem::SettingItem(SettingItem {
8819                title: "Auto Replace Emoji Shortcode",
8820                description: "Whether to automatically replace emoji shortcodes with emoji characters.",
8821                field: Box::new(SettingField {
8822                    json_path: Some("message_editor.auto_replace_emoji_shortcode"),
8823                    pick: |settings_content| {
8824                        settings_content
8825                            .message_editor
8826                            .as_ref()
8827                            .and_then(|message_editor| {
8828                                message_editor.auto_replace_emoji_shortcode.as_ref()
8829                            })
8830                    },
8831                    write: |settings_content, value| {
8832                        settings_content
8833                            .message_editor
8834                            .get_or_insert_default()
8835                            .auto_replace_emoji_shortcode = value;
8836                    },
8837                }),
8838                metadata: None,
8839                files: USER,
8840            }),
8841            SettingsPageItem::SettingItem(SettingItem {
8842                title: "Drop Size Target",
8843                description: "Relative size of the drop target in the editor that will open dropped file as a split pane.",
8844                field: Box::new(SettingField {
8845                    json_path: Some("drop_target_size"),
8846                    pick: |settings_content| settings_content.workspace.drop_target_size.as_ref(),
8847                    write: |settings_content, value| {
8848                        settings_content.workspace.drop_target_size = value;
8849                    },
8850                }),
8851                metadata: None,
8852                files: USER,
8853            }),
8854        ]
8855    }
8856
8857    let is_global = active_language().is_none();
8858
8859    let lsp_document_colors_item = [SettingsPageItem::SettingItem(SettingItem {
8860        title: "LSP Document Colors",
8861        description: "How to render LSP color previews in the editor.",
8862        field: Box::new(SettingField {
8863            json_path: Some("lsp_document_colors"),
8864            pick: |settings_content| settings_content.editor.lsp_document_colors.as_ref(),
8865            write: |settings_content, value| {
8866                settings_content.editor.lsp_document_colors = value;
8867            },
8868        }),
8869        metadata: None,
8870        files: USER,
8871    })];
8872
8873    if is_global {
8874        concat_sections!(
8875            indentation_section(),
8876            wrapping_section(),
8877            indent_guides_section(),
8878            formatting_section(),
8879            autoclose_section(),
8880            whitespace_section(),
8881            completions_section(),
8882            inlay_hints_section(),
8883            lsp_document_colors_item,
8884            tasks_section(),
8885            miscellaneous_section(),
8886            global_only_miscellaneous_sub_section(),
8887        )
8888    } else {
8889        concat_sections!(
8890            indentation_section(),
8891            wrapping_section(),
8892            indent_guides_section(),
8893            formatting_section(),
8894            autoclose_section(),
8895            whitespace_section(),
8896            completions_section(),
8897            inlay_hints_section(),
8898            tasks_section(),
8899            miscellaneous_section(),
8900        )
8901    }
8902}
8903
8904/// LanguageSettings items that should be included in the "Languages & Tools" page
8905/// not the "Editor" page
8906fn non_editor_language_settings_data() -> Box<[SettingsPageItem]> {
8907    fn lsp_section() -> [SettingsPageItem; 8] {
8908        [
8909            SettingsPageItem::SectionHeader("LSP"),
8910            SettingsPageItem::SettingItem(SettingItem {
8911                title: "Enable Language Server",
8912                description: "Whether to use language servers to provide code intelligence.",
8913                field: Box::new(SettingField {
8914                    json_path: Some("languages.$(language).enable_language_server"),
8915                    pick: |settings_content| {
8916                        language_settings_field(settings_content, |language| {
8917                            language.enable_language_server.as_ref()
8918                        })
8919                    },
8920                    write: |settings_content, value| {
8921                        language_settings_field_mut(settings_content, value, |language, value| {
8922                            language.enable_language_server = value;
8923                        })
8924                    },
8925                }),
8926                metadata: None,
8927                files: USER | PROJECT,
8928            }),
8929            SettingsPageItem::SettingItem(SettingItem {
8930                title: "Language Servers",
8931                description: "The list of language servers to use (or disable) for this language.",
8932                field: Box::new(
8933                    SettingField {
8934                        json_path: Some("languages.$(language).language_servers"),
8935                        pick: |settings_content| {
8936                            language_settings_field(settings_content, |language| {
8937                                language.language_servers.as_ref()
8938                            })
8939                        },
8940                        write: |settings_content, value| {
8941                            language_settings_field_mut(
8942                                settings_content,
8943                                value,
8944                                |language, value| {
8945                                    language.language_servers = value;
8946                                },
8947                            )
8948                        },
8949                    }
8950                    .unimplemented(),
8951                ),
8952                metadata: None,
8953                files: USER | PROJECT,
8954            }),
8955            SettingsPageItem::SettingItem(SettingItem {
8956                title: "Linked Edits",
8957                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.",
8958                field: Box::new(SettingField {
8959                    json_path: Some("languages.$(language).linked_edits"),
8960                    pick: |settings_content| {
8961                        language_settings_field(settings_content, |language| {
8962                            language.linked_edits.as_ref()
8963                        })
8964                    },
8965                    write: |settings_content, value| {
8966                        language_settings_field_mut(settings_content, value, |language, value| {
8967                            language.linked_edits = value;
8968                        })
8969                    },
8970                }),
8971                metadata: None,
8972                files: USER | PROJECT,
8973            }),
8974            SettingsPageItem::SettingItem(SettingItem {
8975                title: "Go To Definition Fallback",
8976                description: "Whether to follow-up empty Go to definition responses from the language server.",
8977                field: Box::new(SettingField {
8978                    json_path: Some("go_to_definition_fallback"),
8979                    pick: |settings_content| {
8980                        settings_content.editor.go_to_definition_fallback.as_ref()
8981                    },
8982                    write: |settings_content, value| {
8983                        settings_content.editor.go_to_definition_fallback = value;
8984                    },
8985                }),
8986                metadata: None,
8987                files: USER,
8988            }),
8989            SettingsPageItem::SettingItem(SettingItem {
8990                title: "Semantic Tokens",
8991                description: {
8992                    static DESCRIPTION: OnceLock<&'static str> = OnceLock::new();
8993                    DESCRIPTION.get_or_init(|| {
8994                        SemanticTokens::VARIANTS
8995                            .iter()
8996                            .filter_map(|v| {
8997                                v.get_documentation().map(|doc| format!("{v:?}: {doc}"))
8998                            })
8999                            .join("\n")
9000                            .leak()
9001                    })
9002                },
9003                field: Box::new(SettingField {
9004                    json_path: Some("languages.$(language).semantic_tokens"),
9005                    pick: |settings_content| {
9006                        settings_content
9007                            .project
9008                            .all_languages
9009                            .defaults
9010                            .semantic_tokens
9011                            .as_ref()
9012                    },
9013                    write: |settings_content, value| {
9014                        settings_content
9015                            .project
9016                            .all_languages
9017                            .defaults
9018                            .semantic_tokens = value;
9019                    },
9020                }),
9021                metadata: None,
9022                files: USER | PROJECT,
9023            }),
9024            SettingsPageItem::SettingItem(SettingItem {
9025                title: "LSP Folding Ranges",
9026                description: "When enabled, use folding ranges from the language server instead of indent-based folding.",
9027                field: Box::new(SettingField {
9028                    json_path: Some("languages.$(language).document_folding_ranges"),
9029                    pick: |settings_content| {
9030                        language_settings_field(settings_content, |language| {
9031                            language.document_folding_ranges.as_ref()
9032                        })
9033                    },
9034                    write: |settings_content, value| {
9035                        language_settings_field_mut(settings_content, value, |language, value| {
9036                            language.document_folding_ranges = value;
9037                        })
9038                    },
9039                }),
9040                metadata: None,
9041                files: USER | PROJECT,
9042            }),
9043            SettingsPageItem::SettingItem(SettingItem {
9044                title: "LSP Document Symbols",
9045                description: "When enabled, use the language server's document symbols for outlines and breadcrumbs instead of tree-sitter.",
9046                field: Box::new(SettingField {
9047                    json_path: Some("languages.$(language).document_symbols"),
9048                    pick: |settings_content| {
9049                        language_settings_field(settings_content, |language| {
9050                            language.document_symbols.as_ref()
9051                        })
9052                    },
9053                    write: |settings_content, value| {
9054                        language_settings_field_mut(settings_content, value, |language, value| {
9055                            language.document_symbols = value;
9056                        })
9057                    },
9058                }),
9059                metadata: None,
9060                files: USER | PROJECT,
9061            }),
9062        ]
9063    }
9064
9065    fn lsp_completions_section() -> [SettingsPageItem; 4] {
9066        [
9067            SettingsPageItem::SectionHeader("LSP Completions"),
9068            SettingsPageItem::SettingItem(SettingItem {
9069                title: "Enabled",
9070                description: "Whether to fetch LSP completions or not.",
9071                field: Box::new(SettingField {
9072                    json_path: Some("languages.$(language).completions.lsp"),
9073                    pick: |settings_content| {
9074                        language_settings_field(settings_content, |language| {
9075                            language.completions.as_ref()?.lsp.as_ref()
9076                        })
9077                    },
9078                    write: |settings_content, value| {
9079                        language_settings_field_mut(settings_content, value, |language, value| {
9080                            language.completions.get_or_insert_default().lsp = value;
9081                        })
9082                    },
9083                }),
9084                metadata: None,
9085                files: USER | PROJECT,
9086            }),
9087            SettingsPageItem::SettingItem(SettingItem {
9088                title: "Fetch Timeout (milliseconds)",
9089                description: "When fetching LSP completions, determines how long to wait for a response of a particular server (set to 0 to wait indefinitely).",
9090                field: Box::new(SettingField {
9091                    json_path: Some("languages.$(language).completions.lsp_fetch_timeout_ms"),
9092                    pick: |settings_content| {
9093                        language_settings_field(settings_content, |language| {
9094                            language.completions.as_ref()?.lsp_fetch_timeout_ms.as_ref()
9095                        })
9096                    },
9097                    write: |settings_content, value| {
9098                        language_settings_field_mut(settings_content, value, |language, value| {
9099                            language
9100                                .completions
9101                                .get_or_insert_default()
9102                                .lsp_fetch_timeout_ms = value;
9103                        })
9104                    },
9105                }),
9106                metadata: None,
9107                files: USER | PROJECT,
9108            }),
9109            SettingsPageItem::SettingItem(SettingItem {
9110                title: "Insert Mode",
9111                description: "Controls how LSP completions are inserted.",
9112                field: Box::new(SettingField {
9113                    json_path: Some("languages.$(language).completions.lsp_insert_mode"),
9114                    pick: |settings_content| {
9115                        language_settings_field(settings_content, |language| {
9116                            language.completions.as_ref()?.lsp_insert_mode.as_ref()
9117                        })
9118                    },
9119                    write: |settings_content, value| {
9120                        language_settings_field_mut(settings_content, value, |language, value| {
9121                            language.completions.get_or_insert_default().lsp_insert_mode = value;
9122                        })
9123                    },
9124                }),
9125                metadata: None,
9126                files: USER | PROJECT,
9127            }),
9128        ]
9129    }
9130
9131    fn debugger_section() -> [SettingsPageItem; 2] {
9132        [
9133            SettingsPageItem::SectionHeader("Debuggers"),
9134            SettingsPageItem::SettingItem(SettingItem {
9135                title: "Debuggers",
9136                description: "Preferred debuggers for this language.",
9137                field: Box::new(
9138                    SettingField {
9139                        json_path: Some("languages.$(language).debuggers"),
9140                        pick: |settings_content| {
9141                            language_settings_field(settings_content, |language| {
9142                                language.debuggers.as_ref()
9143                            })
9144                        },
9145                        write: |settings_content, value| {
9146                            language_settings_field_mut(
9147                                settings_content,
9148                                value,
9149                                |language, value| {
9150                                    language.debuggers = value;
9151                                },
9152                            )
9153                        },
9154                    }
9155                    .unimplemented(),
9156                ),
9157                metadata: None,
9158                files: USER | PROJECT,
9159            }),
9160        ]
9161    }
9162
9163    fn prettier_section() -> [SettingsPageItem; 5] {
9164        [
9165            SettingsPageItem::SectionHeader("Prettier"),
9166            SettingsPageItem::SettingItem(SettingItem {
9167                title: "Allowed",
9168                description: "Enables or disables formatting with Prettier for a given language.",
9169                field: Box::new(SettingField {
9170                    json_path: Some("languages.$(language).prettier.allowed"),
9171                    pick: |settings_content| {
9172                        language_settings_field(settings_content, |language| {
9173                            language.prettier.as_ref()?.allowed.as_ref()
9174                        })
9175                    },
9176                    write: |settings_content, value| {
9177                        language_settings_field_mut(settings_content, value, |language, value| {
9178                            language.prettier.get_or_insert_default().allowed = value;
9179                        })
9180                    },
9181                }),
9182                metadata: None,
9183                files: USER | PROJECT,
9184            }),
9185            SettingsPageItem::SettingItem(SettingItem {
9186                title: "Parser",
9187                description: "Forces Prettier integration to use a specific parser name when formatting files with the language.",
9188                field: Box::new(SettingField {
9189                    json_path: Some("languages.$(language).prettier.parser"),
9190                    pick: |settings_content| {
9191                        language_settings_field(settings_content, |language| {
9192                            language.prettier.as_ref()?.parser.as_ref()
9193                        })
9194                    },
9195                    write: |settings_content, value| {
9196                        language_settings_field_mut(settings_content, value, |language, value| {
9197                            language.prettier.get_or_insert_default().parser = value;
9198                        })
9199                    },
9200                }),
9201                metadata: None,
9202                files: USER | PROJECT,
9203            }),
9204            SettingsPageItem::SettingItem(SettingItem {
9205                title: "Plugins",
9206                description: "Forces Prettier integration to use specific plugins when formatting files with the language.",
9207                field: Box::new(
9208                    SettingField {
9209                        json_path: Some("languages.$(language).prettier.plugins"),
9210                        pick: |settings_content| {
9211                            language_settings_field(settings_content, |language| {
9212                                language.prettier.as_ref()?.plugins.as_ref()
9213                            })
9214                        },
9215                        write: |settings_content, value| {
9216                            language_settings_field_mut(
9217                                settings_content,
9218                                value,
9219                                |language, value| {
9220                                    language.prettier.get_or_insert_default().plugins = value;
9221                                },
9222                            )
9223                        },
9224                    }
9225                    .unimplemented(),
9226                ),
9227                metadata: None,
9228                files: USER | PROJECT,
9229            }),
9230            SettingsPageItem::SettingItem(SettingItem {
9231                title: "Options",
9232                description: "Default Prettier options, in the format as in package.json section for Prettier.",
9233                field: Box::new(
9234                    SettingField {
9235                        json_path: Some("languages.$(language).prettier.options"),
9236                        pick: |settings_content| {
9237                            language_settings_field(settings_content, |language| {
9238                                language.prettier.as_ref()?.options.as_ref()
9239                            })
9240                        },
9241                        write: |settings_content, value| {
9242                            language_settings_field_mut(
9243                                settings_content,
9244                                value,
9245                                |language, value| {
9246                                    language.prettier.get_or_insert_default().options = value;
9247                                },
9248                            )
9249                        },
9250                    }
9251                    .unimplemented(),
9252                ),
9253                metadata: None,
9254                files: USER | PROJECT,
9255            }),
9256        ]
9257    }
9258
9259    concat_sections!(
9260        lsp_section(),
9261        lsp_completions_section(),
9262        debugger_section(),
9263        prettier_section(),
9264    )
9265}
9266
9267fn edit_prediction_language_settings_section() -> [SettingsPageItem; 4] {
9268    [
9269        SettingsPageItem::SectionHeader("Edit Predictions"),
9270        SettingsPageItem::SubPageLink(SubPageLink {
9271            title: "Configure Providers".into(),
9272            r#type: Default::default(),
9273            json_path: Some("edit_predictions.providers"),
9274            description: Some("Set up different edit prediction providers in complement to Zed's built-in Zeta model.".into()),
9275            in_json: false,
9276            files: USER,
9277            render: render_edit_prediction_setup_page
9278        }),
9279        SettingsPageItem::SettingItem(SettingItem {
9280            title: "Show Edit Predictions",
9281            description: "Controls whether edit predictions are shown immediately or manually.",
9282            field: Box::new(SettingField {
9283                json_path: Some("languages.$(language).show_edit_predictions"),
9284                pick: |settings_content| {
9285                    language_settings_field(settings_content, |language| {
9286                        language.show_edit_predictions.as_ref()
9287                    })
9288                },
9289                write: |settings_content, value| {
9290                    language_settings_field_mut(settings_content, value, |language, value| {
9291                        language.show_edit_predictions = value;
9292                    })
9293                },
9294            }),
9295            metadata: None,
9296            files: USER | PROJECT,
9297        }),
9298        SettingsPageItem::SettingItem(SettingItem {
9299            title: "Disable in Language Scopes",
9300            description: "Controls whether edit predictions are shown in the given language scopes.",
9301            field: Box::new(
9302                SettingField {
9303                    json_path: Some("languages.$(language).edit_predictions_disabled_in"),
9304                    pick: |settings_content| {
9305                        language_settings_field(settings_content, |language| {
9306                            language.edit_predictions_disabled_in.as_ref()
9307                        })
9308                    },
9309                    write: |settings_content, value| {
9310                        language_settings_field_mut(settings_content, value, |language, value| {
9311                            language.edit_predictions_disabled_in = value;
9312                        })
9313                    },
9314                }
9315                .unimplemented(),
9316            ),
9317            metadata: None,
9318            files: USER | PROJECT,
9319        }),
9320    ]
9321}
9322
9323fn show_scrollbar_or_editor(
9324    settings_content: &SettingsContent,
9325    show: fn(&SettingsContent) -> Option<&settings::ShowScrollbar>,
9326) -> Option<&settings::ShowScrollbar> {
9327    show(settings_content).or(settings_content
9328        .editor
9329        .scrollbar
9330        .as_ref()
9331        .and_then(|scrollbar| scrollbar.show.as_ref()))
9332}
9333
9334fn dynamic_variants<T>() -> &'static [T::Discriminant]
9335where
9336    T: strum::IntoDiscriminant,
9337    T::Discriminant: strum::VariantArray,
9338{
9339    <<T as strum::IntoDiscriminant>::Discriminant as strum::VariantArray>::VARIANTS
9340}
9341
9342/// Updates the `vim_mode` setting, disabling `helix_mode` if present and
9343/// `vim_mode` is being enabled.
9344fn write_vim_mode(settings: &mut SettingsContent, value: Option<bool>) {
9345    if value == Some(true) && settings.helix_mode == Some(true) {
9346        settings.helix_mode = Some(false);
9347    }
9348    settings.vim_mode = value;
9349}
9350
9351/// Updates the `helix_mode` setting, disabling `vim_mode` if present and
9352/// `helix_mode` is being enabled.
9353fn write_helix_mode(settings: &mut SettingsContent, value: Option<bool>) {
9354    if value == Some(true) && settings.vim_mode == Some(true) {
9355        settings.vim_mode = Some(false);
9356    }
9357    settings.helix_mode = value;
9358}
9359
9360#[cfg(test)]
9361mod tests {
9362    use super::*;
9363
9364    #[test]
9365    fn test_write_vim_helix_mode() {
9366        // Enabling vim mode while `vim_mode` and `helix_mode` are not yet set
9367        // should only update the `vim_mode` setting.
9368        let mut settings = SettingsContent::default();
9369        write_vim_mode(&mut settings, Some(true));
9370        assert_eq!(settings.vim_mode, Some(true));
9371        assert_eq!(settings.helix_mode, None);
9372
9373        // Enabling helix mode while `vim_mode` and `helix_mode` are not yet set
9374        // should only update the `helix_mode` setting.
9375        let mut settings = SettingsContent::default();
9376        write_helix_mode(&mut settings, Some(true));
9377        assert_eq!(settings.helix_mode, Some(true));
9378        assert_eq!(settings.vim_mode, None);
9379
9380        // Disabling helix mode should only touch `helix_mode` setting when
9381        // `vim_mode` is not set.
9382        write_helix_mode(&mut settings, Some(false));
9383        assert_eq!(settings.helix_mode, Some(false));
9384        assert_eq!(settings.vim_mode, None);
9385
9386        // Enabling vim mode should update `vim_mode` but leave `helix_mode`
9387        // untouched.
9388        write_vim_mode(&mut settings, Some(true));
9389        assert_eq!(settings.vim_mode, Some(true));
9390        assert_eq!(settings.helix_mode, Some(false));
9391
9392        // Enabling helix mode should update `helix_mode` and disable
9393        // `vim_mode`.
9394        write_helix_mode(&mut settings, Some(true));
9395        assert_eq!(settings.helix_mode, Some(true));
9396        assert_eq!(settings.vim_mode, Some(false));
9397
9398        // Enabling vim mode should update `vim_mode` and disable
9399        // `helix_mode`.
9400        write_vim_mode(&mut settings, Some(true));
9401        assert_eq!(settings.vim_mode, Some(true));
9402        assert_eq!(settings.helix_mode, Some(false));
9403    }
9404}