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