page_data.rs

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