page_data.rs

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