page_data.rs

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