page_data.rs

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