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