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