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