page_data.rs

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