page_data.rs

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