page_data.rs

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