page_data.rs

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