page_data.rs

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