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