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; 12] {
2414        [
2415            SettingsPageItem::SectionHeader("Vim"),
2416            SettingsPageItem::SettingItem(SettingItem {
2417                title: "Default Mode",
2418                description: "The default mode when Vim starts.",
2419                field: Box::new(SettingField {
2420                    json_path: Some("vim.default_mode"),
2421                    pick: |settings_content| settings_content.vim.as_ref()?.default_mode.as_ref(),
2422                    write: |settings_content, value| {
2423                        settings_content.vim.get_or_insert_default().default_mode = value;
2424                    },
2425                }),
2426                metadata: None,
2427                files: USER,
2428            }),
2429            SettingsPageItem::SettingItem(SettingItem {
2430                title: "Toggle Relative Line Numbers",
2431                description: "Toggle relative line numbers in Vim mode.",
2432                field: Box::new(SettingField {
2433                    json_path: Some("vim.toggle_relative_line_numbers"),
2434                    pick: |settings_content| {
2435                        settings_content
2436                            .vim
2437                            .as_ref()?
2438                            .toggle_relative_line_numbers
2439                            .as_ref()
2440                    },
2441                    write: |settings_content, value| {
2442                        settings_content
2443                            .vim
2444                            .get_or_insert_default()
2445                            .toggle_relative_line_numbers = value;
2446                    },
2447                }),
2448                metadata: None,
2449                files: USER,
2450            }),
2451            SettingsPageItem::SettingItem(SettingItem {
2452                title: "Use System Clipboard",
2453                description: "Controls when to use system clipboard in Vim mode.",
2454                field: Box::new(SettingField {
2455                    json_path: Some("vim.use_system_clipboard"),
2456                    pick: |settings_content| {
2457                        settings_content.vim.as_ref()?.use_system_clipboard.as_ref()
2458                    },
2459                    write: |settings_content, value| {
2460                        settings_content
2461                            .vim
2462                            .get_or_insert_default()
2463                            .use_system_clipboard = value;
2464                    },
2465                }),
2466                metadata: None,
2467                files: USER,
2468            }),
2469            SettingsPageItem::SettingItem(SettingItem {
2470                title: "Use Smartcase Find",
2471                description: "Enable smartcase searching in Vim mode.",
2472                field: Box::new(SettingField {
2473                    json_path: Some("vim.use_smartcase_find"),
2474                    pick: |settings_content| {
2475                        settings_content.vim.as_ref()?.use_smartcase_find.as_ref()
2476                    },
2477                    write: |settings_content, value| {
2478                        settings_content
2479                            .vim
2480                            .get_or_insert_default()
2481                            .use_smartcase_find = value;
2482                    },
2483                }),
2484                metadata: None,
2485                files: USER,
2486            }),
2487            SettingsPageItem::SettingItem(SettingItem {
2488                title: "Global Substitution Default",
2489                description: "When enabled, the :substitute command replaces all matches in a line by default. The 'g' flag then toggles this behavior.",
2490                field: Box::new(SettingField {
2491                    json_path: Some("vim.gdefault"),
2492                    pick: |settings_content| settings_content.vim.as_ref()?.gdefault.as_ref(),
2493                    write: |settings_content, value| {
2494                        settings_content.vim.get_or_insert_default().gdefault = value;
2495                    },
2496                }),
2497                metadata: None,
2498                files: USER,
2499            }),
2500            SettingsPageItem::SettingItem(SettingItem {
2501                title: "Highlight on Yank Duration",
2502                description: "Duration in milliseconds to highlight yanked text in Vim mode.",
2503                field: Box::new(SettingField {
2504                    json_path: Some("vim.highlight_on_yank_duration"),
2505                    pick: |settings_content| {
2506                        settings_content
2507                            .vim
2508                            .as_ref()?
2509                            .highlight_on_yank_duration
2510                            .as_ref()
2511                    },
2512                    write: |settings_content, value| {
2513                        settings_content
2514                            .vim
2515                            .get_or_insert_default()
2516                            .highlight_on_yank_duration = value;
2517                    },
2518                }),
2519                metadata: None,
2520                files: USER,
2521            }),
2522            SettingsPageItem::SettingItem(SettingItem {
2523                title: "Cursor Shape - Normal Mode",
2524                description: "Cursor shape for normal mode.",
2525                field: Box::new(SettingField {
2526                    json_path: Some("vim.cursor_shape.normal"),
2527                    pick: |settings_content| {
2528                        settings_content
2529                            .vim
2530                            .as_ref()?
2531                            .cursor_shape
2532                            .as_ref()?
2533                            .normal
2534                            .as_ref()
2535                    },
2536                    write: |settings_content, value| {
2537                        settings_content
2538                            .vim
2539                            .get_or_insert_default()
2540                            .cursor_shape
2541                            .get_or_insert_default()
2542                            .normal = value;
2543                    },
2544                }),
2545                metadata: None,
2546                files: USER,
2547            }),
2548            SettingsPageItem::SettingItem(SettingItem {
2549                title: "Cursor Shape - Insert Mode",
2550                description: "Cursor shape for insert mode. Inherit uses the editor's cursor shape.",
2551                field: Box::new(SettingField {
2552                    json_path: Some("vim.cursor_shape.insert"),
2553                    pick: |settings_content| {
2554                        settings_content
2555                            .vim
2556                            .as_ref()?
2557                            .cursor_shape
2558                            .as_ref()?
2559                            .insert
2560                            .as_ref()
2561                    },
2562                    write: |settings_content, value| {
2563                        settings_content
2564                            .vim
2565                            .get_or_insert_default()
2566                            .cursor_shape
2567                            .get_or_insert_default()
2568                            .insert = value;
2569                    },
2570                }),
2571                metadata: None,
2572                files: USER,
2573            }),
2574            SettingsPageItem::SettingItem(SettingItem {
2575                title: "Cursor Shape - Replace Mode",
2576                description: "Cursor shape for replace mode.",
2577                field: Box::new(SettingField {
2578                    json_path: Some("vim.cursor_shape.replace"),
2579                    pick: |settings_content| {
2580                        settings_content
2581                            .vim
2582                            .as_ref()?
2583                            .cursor_shape
2584                            .as_ref()?
2585                            .replace
2586                            .as_ref()
2587                    },
2588                    write: |settings_content, value| {
2589                        settings_content
2590                            .vim
2591                            .get_or_insert_default()
2592                            .cursor_shape
2593                            .get_or_insert_default()
2594                            .replace = value;
2595                    },
2596                }),
2597                metadata: None,
2598                files: USER,
2599            }),
2600            SettingsPageItem::SettingItem(SettingItem {
2601                title: "Cursor Shape - Visual Mode",
2602                description: "Cursor shape for visual mode.",
2603                field: Box::new(SettingField {
2604                    json_path: Some("vim.cursor_shape.visual"),
2605                    pick: |settings_content| {
2606                        settings_content
2607                            .vim
2608                            .as_ref()?
2609                            .cursor_shape
2610                            .as_ref()?
2611                            .visual
2612                            .as_ref()
2613                    },
2614                    write: |settings_content, value| {
2615                        settings_content
2616                            .vim
2617                            .get_or_insert_default()
2618                            .cursor_shape
2619                            .get_or_insert_default()
2620                            .visual = value;
2621                    },
2622                }),
2623                metadata: None,
2624                files: USER,
2625            }),
2626            SettingsPageItem::SettingItem(SettingItem {
2627                title: "Custom Digraphs",
2628                description: "Custom digraph mappings for Vim mode.",
2629                field: Box::new(
2630                    SettingField {
2631                        json_path: Some("vim.custom_digraphs"),
2632                        pick: |settings_content| {
2633                            settings_content.vim.as_ref()?.custom_digraphs.as_ref()
2634                        },
2635                        write: |settings_content, value| {
2636                            settings_content.vim.get_or_insert_default().custom_digraphs = value;
2637                        },
2638                    }
2639                    .unimplemented(),
2640                ),
2641                metadata: None,
2642                files: USER,
2643            }),
2644        ]
2645    }
2646
2647    let items = concat_sections!(
2648        auto_save_section(),
2649        which_key_section(),
2650        multibuffer_section(),
2651        scrolling_section(),
2652        signature_help_section(),
2653        hover_popover_section(),
2654        drag_and_drop_selection_section(),
2655        gutter_section(),
2656        scrollbar_section(),
2657        minimap_section(),
2658        toolbar_section(),
2659        vim_settings_section(),
2660        language_settings_data(),
2661    );
2662
2663    SettingsPage {
2664        title: "Editor",
2665        items: items,
2666    }
2667}
2668
2669fn languages_and_tools_page(cx: &App) -> SettingsPage {
2670    fn file_types_section() -> [SettingsPageItem; 2] {
2671        [
2672            SettingsPageItem::SectionHeader("File Types"),
2673            SettingsPageItem::SettingItem(SettingItem {
2674                title: "File Type Associations",
2675                description: "A mapping from languages to files and file extensions that should be treated as that language.",
2676                field: Box::new(
2677                    SettingField {
2678                        json_path: Some("file_type_associations"),
2679                        pick: |settings_content| {
2680                            settings_content.project.all_languages.file_types.as_ref()
2681                        },
2682                        write: |settings_content, value| {
2683                            settings_content.project.all_languages.file_types = value;
2684                        },
2685                    }
2686                    .unimplemented(),
2687                ),
2688                metadata: None,
2689                files: USER | PROJECT,
2690            }),
2691        ]
2692    }
2693
2694    fn diagnostics_section() -> [SettingsPageItem; 3] {
2695        [
2696            SettingsPageItem::SectionHeader("Diagnostics"),
2697            SettingsPageItem::SettingItem(SettingItem {
2698                title: "Max Severity",
2699                description: "Which level to use to filter out diagnostics displayed in the editor.",
2700                field: Box::new(SettingField {
2701                    json_path: Some("diagnostics_max_severity"),
2702                    pick: |settings_content| {
2703                        settings_content.editor.diagnostics_max_severity.as_ref()
2704                    },
2705                    write: |settings_content, value| {
2706                        settings_content.editor.diagnostics_max_severity = value;
2707                    },
2708                }),
2709                metadata: None,
2710                files: USER,
2711            }),
2712            SettingsPageItem::SettingItem(SettingItem {
2713                title: "Include Warnings",
2714                description: "Whether to show warnings or not by default.",
2715                field: Box::new(SettingField {
2716                    json_path: Some("diagnostics.include_warnings"),
2717                    pick: |settings_content| {
2718                        settings_content
2719                            .diagnostics
2720                            .as_ref()?
2721                            .include_warnings
2722                            .as_ref()
2723                    },
2724                    write: |settings_content, value| {
2725                        settings_content
2726                            .diagnostics
2727                            .get_or_insert_default()
2728                            .include_warnings = value;
2729                    },
2730                }),
2731                metadata: None,
2732                files: USER,
2733            }),
2734        ]
2735    }
2736
2737    fn inline_diagnostics_section() -> [SettingsPageItem; 5] {
2738        [
2739            SettingsPageItem::SectionHeader("Inline Diagnostics"),
2740            SettingsPageItem::SettingItem(SettingItem {
2741                title: "Enabled",
2742                description: "Whether to show diagnostics inline or not.",
2743                field: Box::new(SettingField {
2744                    json_path: Some("diagnostics.inline.enabled"),
2745                    pick: |settings_content| {
2746                        settings_content
2747                            .diagnostics
2748                            .as_ref()?
2749                            .inline
2750                            .as_ref()?
2751                            .enabled
2752                            .as_ref()
2753                    },
2754                    write: |settings_content, value| {
2755                        settings_content
2756                            .diagnostics
2757                            .get_or_insert_default()
2758                            .inline
2759                            .get_or_insert_default()
2760                            .enabled = value;
2761                    },
2762                }),
2763                metadata: None,
2764                files: USER,
2765            }),
2766            SettingsPageItem::SettingItem(SettingItem {
2767                title: "Update Debounce",
2768                description: "The delay in milliseconds to show inline diagnostics after the last diagnostic update.",
2769                field: Box::new(SettingField {
2770                    json_path: Some("diagnostics.inline.update_debounce_ms"),
2771                    pick: |settings_content| {
2772                        settings_content
2773                            .diagnostics
2774                            .as_ref()?
2775                            .inline
2776                            .as_ref()?
2777                            .update_debounce_ms
2778                            .as_ref()
2779                    },
2780                    write: |settings_content, value| {
2781                        settings_content
2782                            .diagnostics
2783                            .get_or_insert_default()
2784                            .inline
2785                            .get_or_insert_default()
2786                            .update_debounce_ms = value;
2787                    },
2788                }),
2789                metadata: None,
2790                files: USER,
2791            }),
2792            SettingsPageItem::SettingItem(SettingItem {
2793                title: "Padding",
2794                description: "The amount of padding between the end of the source line and the start of the inline diagnostic.",
2795                field: Box::new(SettingField {
2796                    json_path: Some("diagnostics.inline.padding"),
2797                    pick: |settings_content| {
2798                        settings_content
2799                            .diagnostics
2800                            .as_ref()?
2801                            .inline
2802                            .as_ref()?
2803                            .padding
2804                            .as_ref()
2805                    },
2806                    write: |settings_content, value| {
2807                        settings_content
2808                            .diagnostics
2809                            .get_or_insert_default()
2810                            .inline
2811                            .get_or_insert_default()
2812                            .padding = value;
2813                    },
2814                }),
2815                metadata: None,
2816                files: USER,
2817            }),
2818            SettingsPageItem::SettingItem(SettingItem {
2819                title: "Minimum Column",
2820                description: "The minimum column at which to display inline diagnostics.",
2821                field: Box::new(SettingField {
2822                    json_path: Some("diagnostics.inline.min_column"),
2823                    pick: |settings_content| {
2824                        settings_content
2825                            .diagnostics
2826                            .as_ref()?
2827                            .inline
2828                            .as_ref()?
2829                            .min_column
2830                            .as_ref()
2831                    },
2832                    write: |settings_content, value| {
2833                        settings_content
2834                            .diagnostics
2835                            .get_or_insert_default()
2836                            .inline
2837                            .get_or_insert_default()
2838                            .min_column = value;
2839                    },
2840                }),
2841                metadata: None,
2842                files: USER,
2843            }),
2844        ]
2845    }
2846
2847    fn lsp_pull_diagnostics_section() -> [SettingsPageItem; 3] {
2848        [
2849            SettingsPageItem::SectionHeader("LSP Pull Diagnostics"),
2850            SettingsPageItem::SettingItem(SettingItem {
2851                title: "Enabled",
2852                description: "Whether to pull for language server-powered diagnostics or not.",
2853                field: Box::new(SettingField {
2854                    json_path: Some("diagnostics.lsp_pull_diagnostics.enabled"),
2855                    pick: |settings_content| {
2856                        settings_content
2857                            .diagnostics
2858                            .as_ref()?
2859                            .lsp_pull_diagnostics
2860                            .as_ref()?
2861                            .enabled
2862                            .as_ref()
2863                    },
2864                    write: |settings_content, value| {
2865                        settings_content
2866                            .diagnostics
2867                            .get_or_insert_default()
2868                            .lsp_pull_diagnostics
2869                            .get_or_insert_default()
2870                            .enabled = value;
2871                    },
2872                }),
2873                metadata: None,
2874                files: USER,
2875            }),
2876            // todo(settings_ui): Needs unit
2877            SettingsPageItem::SettingItem(SettingItem {
2878                title: "Debounce",
2879                description: "Minimum time to wait before pulling diagnostics from the language server(s).",
2880                field: Box::new(SettingField {
2881                    json_path: Some("diagnostics.lsp_pull_diagnostics.debounce_ms"),
2882                    pick: |settings_content| {
2883                        settings_content
2884                            .diagnostics
2885                            .as_ref()?
2886                            .lsp_pull_diagnostics
2887                            .as_ref()?
2888                            .debounce_ms
2889                            .as_ref()
2890                    },
2891                    write: |settings_content, value| {
2892                        settings_content
2893                            .diagnostics
2894                            .get_or_insert_default()
2895                            .lsp_pull_diagnostics
2896                            .get_or_insert_default()
2897                            .debounce_ms = value;
2898                    },
2899                }),
2900                metadata: None,
2901                files: USER,
2902            }),
2903        ]
2904    }
2905
2906    fn lsp_highlights_section() -> [SettingsPageItem; 2] {
2907        [
2908            SettingsPageItem::SectionHeader("LSP Highlights"),
2909            SettingsPageItem::SettingItem(SettingItem {
2910                title: "Debounce",
2911                description: "The debounce delay before querying highlights from the language.",
2912                field: Box::new(SettingField {
2913                    json_path: Some("lsp_highlight_debounce"),
2914                    pick: |settings_content| {
2915                        settings_content.editor.lsp_highlight_debounce.as_ref()
2916                    },
2917                    write: |settings_content, value| {
2918                        settings_content.editor.lsp_highlight_debounce = value;
2919                    },
2920                }),
2921                metadata: None,
2922                files: USER,
2923            }),
2924        ]
2925    }
2926
2927    fn languages_list_section(cx: &App) -> Box<[SettingsPageItem]> {
2928        // todo(settings_ui): Refresh on extension (un)/installed
2929        // Note that `crates/json_schema_store` solves the same problem, there is probably a way to unify the two
2930        std::iter::once(SettingsPageItem::SectionHeader("Languages"))
2931            .chain(all_language_names(cx).into_iter().map(|language_name| {
2932                let link = format!("languages.{language_name}");
2933                SettingsPageItem::SubPageLink(SubPageLink {
2934                    title: language_name,
2935                    r#type: crate::SubPageType::Language,
2936                    description: None,
2937                    json_path: Some(link.leak()),
2938                    in_json: true,
2939                    files: USER | PROJECT,
2940                    render: |this, scroll_handle, window, cx| {
2941                        let items: Box<[SettingsPageItem]> = concat_sections!(
2942                            language_settings_data(),
2943                            non_editor_language_settings_data(),
2944                            edit_prediction_language_settings_section()
2945                        );
2946                        this.render_sub_page_items(
2947                            items.iter().enumerate(),
2948                            scroll_handle,
2949                            window,
2950                            cx,
2951                        )
2952                        .into_any_element()
2953                    },
2954                })
2955            }))
2956            .collect()
2957    }
2958
2959    SettingsPage {
2960        title: "Languages & Tools",
2961        items: {
2962            concat_sections!(
2963                non_editor_language_settings_data(),
2964                file_types_section(),
2965                diagnostics_section(),
2966                inline_diagnostics_section(),
2967                lsp_pull_diagnostics_section(),
2968                lsp_highlights_section(),
2969                languages_list_section(cx),
2970            )
2971        },
2972    }
2973}
2974
2975fn search_and_files_page() -> SettingsPage {
2976    fn search_section() -> [SettingsPageItem; 9] {
2977        [
2978            SettingsPageItem::SectionHeader("Search"),
2979            SettingsPageItem::SettingItem(SettingItem {
2980                title: "Whole Word",
2981                description: "Search for whole words by default.",
2982                field: Box::new(SettingField {
2983                    json_path: Some("search.whole_word"),
2984                    pick: |settings_content| {
2985                        settings_content.editor.search.as_ref()?.whole_word.as_ref()
2986                    },
2987                    write: |settings_content, value| {
2988                        settings_content
2989                            .editor
2990                            .search
2991                            .get_or_insert_default()
2992                            .whole_word = value;
2993                    },
2994                }),
2995                metadata: None,
2996                files: USER,
2997            }),
2998            SettingsPageItem::SettingItem(SettingItem {
2999                title: "Case Sensitive",
3000                description: "Search case-sensitively by default.",
3001                field: Box::new(SettingField {
3002                    json_path: Some("search.case_sensitive"),
3003                    pick: |settings_content| {
3004                        settings_content
3005                            .editor
3006                            .search
3007                            .as_ref()?
3008                            .case_sensitive
3009                            .as_ref()
3010                    },
3011                    write: |settings_content, value| {
3012                        settings_content
3013                            .editor
3014                            .search
3015                            .get_or_insert_default()
3016                            .case_sensitive = value;
3017                    },
3018                }),
3019                metadata: None,
3020                files: USER,
3021            }),
3022            SettingsPageItem::SettingItem(SettingItem {
3023                title: "Use Smartcase Search",
3024                description: "Whether to automatically enable case-sensitive search based on the search query.",
3025                field: Box::new(SettingField {
3026                    json_path: Some("use_smartcase_search"),
3027                    pick: |settings_content| settings_content.editor.use_smartcase_search.as_ref(),
3028                    write: |settings_content, value| {
3029                        settings_content.editor.use_smartcase_search = value;
3030                    },
3031                }),
3032                metadata: None,
3033                files: USER,
3034            }),
3035            SettingsPageItem::SettingItem(SettingItem {
3036                title: "Include Ignored",
3037                description: "Include ignored files in search results by default.",
3038                field: Box::new(SettingField {
3039                    json_path: Some("search.include_ignored"),
3040                    pick: |settings_content| {
3041                        settings_content
3042                            .editor
3043                            .search
3044                            .as_ref()?
3045                            .include_ignored
3046                            .as_ref()
3047                    },
3048                    write: |settings_content, value| {
3049                        settings_content
3050                            .editor
3051                            .search
3052                            .get_or_insert_default()
3053                            .include_ignored = value;
3054                    },
3055                }),
3056                metadata: None,
3057                files: USER,
3058            }),
3059            SettingsPageItem::SettingItem(SettingItem {
3060                title: "Regex",
3061                description: "Use regex search by default.",
3062                field: Box::new(SettingField {
3063                    json_path: Some("search.regex"),
3064                    pick: |settings_content| {
3065                        settings_content.editor.search.as_ref()?.regex.as_ref()
3066                    },
3067                    write: |settings_content, value| {
3068                        settings_content.editor.search.get_or_insert_default().regex = value;
3069                    },
3070                }),
3071                metadata: None,
3072                files: USER,
3073            }),
3074            SettingsPageItem::SettingItem(SettingItem {
3075                title: "Search Wrap",
3076                description: "Whether the editor search results will loop.",
3077                field: Box::new(SettingField {
3078                    json_path: Some("search_wrap"),
3079                    pick: |settings_content| settings_content.editor.search_wrap.as_ref(),
3080                    write: |settings_content, value| {
3081                        settings_content.editor.search_wrap = value;
3082                    },
3083                }),
3084                metadata: None,
3085                files: USER,
3086            }),
3087            SettingsPageItem::SettingItem(SettingItem {
3088                title: "Center on Match",
3089                description: "Whether to center the current match in the editor",
3090                field: Box::new(SettingField {
3091                    json_path: Some("editor.search.center_on_match"),
3092                    pick: |settings_content| {
3093                        settings_content
3094                            .editor
3095                            .search
3096                            .as_ref()
3097                            .and_then(|search| search.center_on_match.as_ref())
3098                    },
3099                    write: |settings_content, value| {
3100                        settings_content
3101                            .editor
3102                            .search
3103                            .get_or_insert_default()
3104                            .center_on_match = value;
3105                    },
3106                }),
3107                metadata: None,
3108                files: USER,
3109            }),
3110            SettingsPageItem::SettingItem(SettingItem {
3111                title: "Seed Search Query From Cursor",
3112                description: "When to populate a new search's query based on the text under the cursor.",
3113                field: Box::new(SettingField {
3114                    json_path: Some("seed_search_query_from_cursor"),
3115                    pick: |settings_content| {
3116                        settings_content
3117                            .editor
3118                            .seed_search_query_from_cursor
3119                            .as_ref()
3120                    },
3121                    write: |settings_content, value| {
3122                        settings_content.editor.seed_search_query_from_cursor = value;
3123                    },
3124                }),
3125                metadata: None,
3126                files: USER,
3127            }),
3128        ]
3129    }
3130
3131    fn file_finder_section() -> [SettingsPageItem; 6] {
3132        [
3133            SettingsPageItem::SectionHeader("File Finder"),
3134            // todo: null by default
3135            SettingsPageItem::SettingItem(SettingItem {
3136                title: "Include Ignored in Search",
3137                description: "Use gitignored files when searching.",
3138                field: Box::new(SettingField {
3139                    json_path: Some("file_finder.include_ignored"),
3140                    pick: |settings_content| {
3141                        settings_content
3142                            .file_finder
3143                            .as_ref()?
3144                            .include_ignored
3145                            .as_ref()
3146                    },
3147                    write: |settings_content, value| {
3148                        settings_content
3149                            .file_finder
3150                            .get_or_insert_default()
3151                            .include_ignored = value;
3152                    },
3153                }),
3154                metadata: None,
3155                files: USER,
3156            }),
3157            SettingsPageItem::SettingItem(SettingItem {
3158                title: "File Icons",
3159                description: "Show file icons in the file finder.",
3160                field: Box::new(SettingField {
3161                    json_path: Some("file_finder.file_icons"),
3162                    pick: |settings_content| {
3163                        settings_content.file_finder.as_ref()?.file_icons.as_ref()
3164                    },
3165                    write: |settings_content, value| {
3166                        settings_content
3167                            .file_finder
3168                            .get_or_insert_default()
3169                            .file_icons = value;
3170                    },
3171                }),
3172                metadata: None,
3173                files: USER,
3174            }),
3175            SettingsPageItem::SettingItem(SettingItem {
3176                title: "Modal Max Width",
3177                description: "Determines how much space the file finder can take up in relation to the available window width.",
3178                field: Box::new(SettingField {
3179                    json_path: Some("file_finder.modal_max_width"),
3180                    pick: |settings_content| {
3181                        settings_content
3182                            .file_finder
3183                            .as_ref()?
3184                            .modal_max_width
3185                            .as_ref()
3186                    },
3187                    write: |settings_content, value| {
3188                        settings_content
3189                            .file_finder
3190                            .get_or_insert_default()
3191                            .modal_max_width = value;
3192                    },
3193                }),
3194                metadata: None,
3195                files: USER,
3196            }),
3197            SettingsPageItem::SettingItem(SettingItem {
3198                title: "Skip Focus For Active In Search",
3199                description: "Whether the file finder should skip focus for the active file in search results.",
3200                field: Box::new(SettingField {
3201                    json_path: Some("file_finder.skip_focus_for_active_in_search"),
3202                    pick: |settings_content| {
3203                        settings_content
3204                            .file_finder
3205                            .as_ref()?
3206                            .skip_focus_for_active_in_search
3207                            .as_ref()
3208                    },
3209                    write: |settings_content, value| {
3210                        settings_content
3211                            .file_finder
3212                            .get_or_insert_default()
3213                            .skip_focus_for_active_in_search = value;
3214                    },
3215                }),
3216                metadata: None,
3217                files: USER,
3218            }),
3219            SettingsPageItem::SettingItem(SettingItem {
3220                title: "Git Status",
3221                description: "Show the Git status in the file finder.",
3222                field: Box::new(SettingField {
3223                    json_path: Some("file_finder.git_status"),
3224                    pick: |settings_content| {
3225                        settings_content.file_finder.as_ref()?.git_status.as_ref()
3226                    },
3227                    write: |settings_content, value| {
3228                        settings_content
3229                            .file_finder
3230                            .get_or_insert_default()
3231                            .git_status = value;
3232                    },
3233                }),
3234                metadata: None,
3235                files: USER,
3236            }),
3237        ]
3238    }
3239
3240    fn file_scan_section() -> [SettingsPageItem; 5] {
3241        [
3242            SettingsPageItem::SectionHeader("File Scan"),
3243            SettingsPageItem::SettingItem(SettingItem {
3244                title: "File Scan Exclusions",
3245                description: "Files or globs of files that will be excluded by Zed entirely. They will be skipped during file scans, file searches, and not be displayed in the project file tree. Takes precedence over \"File Scan Inclusions\"",
3246                field: Box::new(
3247                    SettingField {
3248                        json_path: Some("file_scan_exclusions"),
3249                        pick: |settings_content| {
3250                            settings_content
3251                                .project
3252                                .worktree
3253                                .file_scan_exclusions
3254                                .as_ref()
3255                        },
3256                        write: |settings_content, value| {
3257                            settings_content.project.worktree.file_scan_exclusions = value;
3258                        },
3259                    }
3260                    .unimplemented(),
3261                ),
3262                metadata: None,
3263                files: USER,
3264            }),
3265            SettingsPageItem::SettingItem(SettingItem {
3266                title: "File Scan Inclusions",
3267                description: "Files or globs of files that will be included by Zed, even when ignored by git. This is useful for files that are not tracked by git, but are still important to your project. Note that globs that are overly broad can slow down Zed's file scanning. \"File Scan Exclusions\" takes precedence over these inclusions",
3268                field: Box::new(
3269                    SettingField {
3270                        json_path: Some("file_scan_inclusions"),
3271                        pick: |settings_content| {
3272                            settings_content
3273                                .project
3274                                .worktree
3275                                .file_scan_inclusions
3276                                .as_ref()
3277                        },
3278                        write: |settings_content, value| {
3279                            settings_content.project.worktree.file_scan_inclusions = value;
3280                        },
3281                    }
3282                    .unimplemented(),
3283                ),
3284                metadata: None,
3285                files: USER,
3286            }),
3287            SettingsPageItem::SettingItem(SettingItem {
3288                title: "Restore File State",
3289                description: "Restore previous file state when reopening.",
3290                field: Box::new(SettingField {
3291                    json_path: Some("restore_on_file_reopen"),
3292                    pick: |settings_content| {
3293                        settings_content.workspace.restore_on_file_reopen.as_ref()
3294                    },
3295                    write: |settings_content, value| {
3296                        settings_content.workspace.restore_on_file_reopen = value;
3297                    },
3298                }),
3299                metadata: None,
3300                files: USER,
3301            }),
3302            SettingsPageItem::SettingItem(SettingItem {
3303                title: "Close on File Delete",
3304                description: "Automatically close files that have been deleted.",
3305                field: Box::new(SettingField {
3306                    json_path: Some("close_on_file_delete"),
3307                    pick: |settings_content| {
3308                        settings_content.workspace.close_on_file_delete.as_ref()
3309                    },
3310                    write: |settings_content, value| {
3311                        settings_content.workspace.close_on_file_delete = value;
3312                    },
3313                }),
3314                metadata: None,
3315                files: USER,
3316            }),
3317        ]
3318    }
3319
3320    SettingsPage {
3321        title: "Search & Files",
3322        items: concat_sections![search_section(), file_finder_section(), file_scan_section()],
3323    }
3324}
3325
3326fn window_and_layout_page() -> SettingsPage {
3327    fn status_bar_section() -> [SettingsPageItem; 9] {
3328        [
3329            SettingsPageItem::SectionHeader("Status Bar"),
3330            SettingsPageItem::SettingItem(SettingItem {
3331                title: "Project Panel Button",
3332                description: "Show the project panel button in the status bar.",
3333                field: Box::new(SettingField {
3334                    json_path: Some("project_panel.button"),
3335                    pick: |settings_content| {
3336                        settings_content.project_panel.as_ref()?.button.as_ref()
3337                    },
3338                    write: |settings_content, value| {
3339                        settings_content
3340                            .project_panel
3341                            .get_or_insert_default()
3342                            .button = value;
3343                    },
3344                }),
3345                metadata: None,
3346                files: USER,
3347            }),
3348            SettingsPageItem::SettingItem(SettingItem {
3349                title: "Active Language Button",
3350                description: "Show the active language button in the status bar.",
3351                field: Box::new(SettingField {
3352                    json_path: Some("status_bar.active_language_button"),
3353                    pick: |settings_content| {
3354                        settings_content
3355                            .status_bar
3356                            .as_ref()?
3357                            .active_language_button
3358                            .as_ref()
3359                    },
3360                    write: |settings_content, value| {
3361                        settings_content
3362                            .status_bar
3363                            .get_or_insert_default()
3364                            .active_language_button = value;
3365                    },
3366                }),
3367                metadata: None,
3368                files: USER,
3369            }),
3370            SettingsPageItem::SettingItem(SettingItem {
3371                title: "Active Encoding Button",
3372                description: "Control when to show the active encoding in the status bar.",
3373                field: Box::new(SettingField {
3374                    json_path: Some("status_bar.active_encoding_button"),
3375                    pick: |settings_content| {
3376                        settings_content
3377                            .status_bar
3378                            .as_ref()?
3379                            .active_encoding_button
3380                            .as_ref()
3381                    },
3382                    write: |settings_content, value| {
3383                        settings_content
3384                            .status_bar
3385                            .get_or_insert_default()
3386                            .active_encoding_button = value;
3387                    },
3388                }),
3389                metadata: None,
3390                files: USER,
3391            }),
3392            SettingsPageItem::SettingItem(SettingItem {
3393                title: "Cursor Position Button",
3394                description: "Show the cursor position button in the status bar.",
3395                field: Box::new(SettingField {
3396                    json_path: Some("status_bar.cursor_position_button"),
3397                    pick: |settings_content| {
3398                        settings_content
3399                            .status_bar
3400                            .as_ref()?
3401                            .cursor_position_button
3402                            .as_ref()
3403                    },
3404                    write: |settings_content, value| {
3405                        settings_content
3406                            .status_bar
3407                            .get_or_insert_default()
3408                            .cursor_position_button = value;
3409                    },
3410                }),
3411                metadata: None,
3412                files: USER,
3413            }),
3414            SettingsPageItem::SettingItem(SettingItem {
3415                title: "Terminal Button",
3416                description: "Show the terminal button in the status bar.",
3417                field: Box::new(SettingField {
3418                    json_path: Some("terminal.button"),
3419                    pick: |settings_content| settings_content.terminal.as_ref()?.button.as_ref(),
3420                    write: |settings_content, value| {
3421                        settings_content.terminal.get_or_insert_default().button = value;
3422                    },
3423                }),
3424                metadata: None,
3425                files: USER,
3426            }),
3427            SettingsPageItem::SettingItem(SettingItem {
3428                title: "Diagnostics Button",
3429                description: "Show the project diagnostics button in the status bar.",
3430                field: Box::new(SettingField {
3431                    json_path: Some("diagnostics.button"),
3432                    pick: |settings_content| settings_content.diagnostics.as_ref()?.button.as_ref(),
3433                    write: |settings_content, value| {
3434                        settings_content.diagnostics.get_or_insert_default().button = value;
3435                    },
3436                }),
3437                metadata: None,
3438                files: USER,
3439            }),
3440            SettingsPageItem::SettingItem(SettingItem {
3441                title: "Project Search Button",
3442                description: "Show the project search button in the status bar.",
3443                field: Box::new(SettingField {
3444                    json_path: Some("search.button"),
3445                    pick: |settings_content| {
3446                        settings_content.editor.search.as_ref()?.button.as_ref()
3447                    },
3448                    write: |settings_content, value| {
3449                        settings_content
3450                            .editor
3451                            .search
3452                            .get_or_insert_default()
3453                            .button = value;
3454                    },
3455                }),
3456                metadata: None,
3457                files: USER,
3458            }),
3459            SettingsPageItem::SettingItem(SettingItem {
3460                title: "Debugger Button",
3461                description: "Show the debugger button in the status bar.",
3462                field: Box::new(SettingField {
3463                    json_path: Some("debugger.button"),
3464                    pick: |settings_content| settings_content.debugger.as_ref()?.button.as_ref(),
3465                    write: |settings_content, value| {
3466                        settings_content.debugger.get_or_insert_default().button = value;
3467                    },
3468                }),
3469                metadata: None,
3470                files: USER,
3471            }),
3472        ]
3473    }
3474
3475    fn title_bar_section() -> [SettingsPageItem; 9] {
3476        [
3477            SettingsPageItem::SectionHeader("Title Bar"),
3478            SettingsPageItem::SettingItem(SettingItem {
3479                title: "Show Branch Icon",
3480                description: "Show the branch icon beside branch switcher in the titlebar.",
3481                field: Box::new(SettingField {
3482                    json_path: Some("title_bar.show_branch_icon"),
3483                    pick: |settings_content| {
3484                        settings_content
3485                            .title_bar
3486                            .as_ref()?
3487                            .show_branch_icon
3488                            .as_ref()
3489                    },
3490                    write: |settings_content, value| {
3491                        settings_content
3492                            .title_bar
3493                            .get_or_insert_default()
3494                            .show_branch_icon = value;
3495                    },
3496                }),
3497                metadata: None,
3498                files: USER,
3499            }),
3500            SettingsPageItem::SettingItem(SettingItem {
3501                title: "Show Branch Name",
3502                description: "Show the branch name button in the titlebar.",
3503                field: Box::new(SettingField {
3504                    json_path: Some("title_bar.show_branch_name"),
3505                    pick: |settings_content| {
3506                        settings_content
3507                            .title_bar
3508                            .as_ref()?
3509                            .show_branch_name
3510                            .as_ref()
3511                    },
3512                    write: |settings_content, value| {
3513                        settings_content
3514                            .title_bar
3515                            .get_or_insert_default()
3516                            .show_branch_name = value;
3517                    },
3518                }),
3519                metadata: None,
3520                files: USER,
3521            }),
3522            SettingsPageItem::SettingItem(SettingItem {
3523                title: "Show Project Items",
3524                description: "Show the project host and name in the titlebar.",
3525                field: Box::new(SettingField {
3526                    json_path: Some("title_bar.show_project_items"),
3527                    pick: |settings_content| {
3528                        settings_content
3529                            .title_bar
3530                            .as_ref()?
3531                            .show_project_items
3532                            .as_ref()
3533                    },
3534                    write: |settings_content, value| {
3535                        settings_content
3536                            .title_bar
3537                            .get_or_insert_default()
3538                            .show_project_items = value;
3539                    },
3540                }),
3541                metadata: None,
3542                files: USER,
3543            }),
3544            SettingsPageItem::SettingItem(SettingItem {
3545                title: "Show Onboarding Banner",
3546                description: "Show banners announcing new features in the titlebar.",
3547                field: Box::new(SettingField {
3548                    json_path: Some("title_bar.show_onboarding_banner"),
3549                    pick: |settings_content| {
3550                        settings_content
3551                            .title_bar
3552                            .as_ref()?
3553                            .show_onboarding_banner
3554                            .as_ref()
3555                    },
3556                    write: |settings_content, value| {
3557                        settings_content
3558                            .title_bar
3559                            .get_or_insert_default()
3560                            .show_onboarding_banner = value;
3561                    },
3562                }),
3563                metadata: None,
3564                files: USER,
3565            }),
3566            SettingsPageItem::SettingItem(SettingItem {
3567                title: "Show Sign In",
3568                description: "Show the sign in button in the titlebar.",
3569                field: Box::new(SettingField {
3570                    json_path: Some("title_bar.show_sign_in"),
3571                    pick: |settings_content| {
3572                        settings_content.title_bar.as_ref()?.show_sign_in.as_ref()
3573                    },
3574                    write: |settings_content, value| {
3575                        settings_content
3576                            .title_bar
3577                            .get_or_insert_default()
3578                            .show_sign_in = value;
3579                    },
3580                }),
3581                metadata: None,
3582                files: USER,
3583            }),
3584            SettingsPageItem::SettingItem(SettingItem {
3585                title: "Show User Menu",
3586                description: "Show the user menu button in the titlebar.",
3587                field: Box::new(SettingField {
3588                    json_path: Some("title_bar.show_user_menu"),
3589                    pick: |settings_content| {
3590                        settings_content.title_bar.as_ref()?.show_user_menu.as_ref()
3591                    },
3592                    write: |settings_content, value| {
3593                        settings_content
3594                            .title_bar
3595                            .get_or_insert_default()
3596                            .show_user_menu = value;
3597                    },
3598                }),
3599                metadata: None,
3600                files: USER,
3601            }),
3602            SettingsPageItem::SettingItem(SettingItem {
3603                title: "Show User Picture",
3604                description: "Show user picture in the titlebar.",
3605                field: Box::new(SettingField {
3606                    json_path: Some("title_bar.show_user_picture"),
3607                    pick: |settings_content| {
3608                        settings_content
3609                            .title_bar
3610                            .as_ref()?
3611                            .show_user_picture
3612                            .as_ref()
3613                    },
3614                    write: |settings_content, value| {
3615                        settings_content
3616                            .title_bar
3617                            .get_or_insert_default()
3618                            .show_user_picture = value;
3619                    },
3620                }),
3621                metadata: None,
3622                files: USER,
3623            }),
3624            SettingsPageItem::SettingItem(SettingItem {
3625                title: "Show Menus",
3626                description: "Show the menus in the titlebar.",
3627                field: Box::new(SettingField {
3628                    json_path: Some("title_bar.show_menus"),
3629                    pick: |settings_content| {
3630                        settings_content.title_bar.as_ref()?.show_menus.as_ref()
3631                    },
3632                    write: |settings_content, value| {
3633                        settings_content
3634                            .title_bar
3635                            .get_or_insert_default()
3636                            .show_menus = value;
3637                    },
3638                }),
3639                metadata: None,
3640                files: USER,
3641            }),
3642        ]
3643    }
3644
3645    fn tab_bar_section() -> [SettingsPageItem; 9] {
3646        [
3647            SettingsPageItem::SectionHeader("Tab Bar"),
3648            SettingsPageItem::SettingItem(SettingItem {
3649                title: "Show Tab Bar",
3650                description: "Show the tab bar in the editor.",
3651                field: Box::new(SettingField {
3652                    json_path: Some("tab_bar.show"),
3653                    pick: |settings_content| settings_content.tab_bar.as_ref()?.show.as_ref(),
3654                    write: |settings_content, value| {
3655                        settings_content.tab_bar.get_or_insert_default().show = value;
3656                    },
3657                }),
3658                metadata: None,
3659                files: USER,
3660            }),
3661            SettingsPageItem::SettingItem(SettingItem {
3662                title: "Show Git Status In Tabs",
3663                description: "Show the Git file status on a tab item.",
3664                field: Box::new(SettingField {
3665                    json_path: Some("tabs.git_status"),
3666                    pick: |settings_content| settings_content.tabs.as_ref()?.git_status.as_ref(),
3667                    write: |settings_content, value| {
3668                        settings_content.tabs.get_or_insert_default().git_status = value;
3669                    },
3670                }),
3671                metadata: None,
3672                files: USER,
3673            }),
3674            SettingsPageItem::SettingItem(SettingItem {
3675                title: "Show File Icons In Tabs",
3676                description: "Show the file icon for a tab.",
3677                field: Box::new(SettingField {
3678                    json_path: Some("tabs.file_icons"),
3679                    pick: |settings_content| settings_content.tabs.as_ref()?.file_icons.as_ref(),
3680                    write: |settings_content, value| {
3681                        settings_content.tabs.get_or_insert_default().file_icons = value;
3682                    },
3683                }),
3684                metadata: None,
3685                files: USER,
3686            }),
3687            SettingsPageItem::SettingItem(SettingItem {
3688                title: "Tab Close Position",
3689                description: "Position of the close button in a tab.",
3690                field: Box::new(SettingField {
3691                    json_path: Some("tabs.close_position"),
3692                    pick: |settings_content| {
3693                        settings_content.tabs.as_ref()?.close_position.as_ref()
3694                    },
3695                    write: |settings_content, value| {
3696                        settings_content.tabs.get_or_insert_default().close_position = value;
3697                    },
3698                }),
3699                metadata: None,
3700                files: USER,
3701            }),
3702            SettingsPageItem::SettingItem(SettingItem {
3703                files: USER,
3704                title: "Maximum Tabs",
3705                description: "Maximum open tabs in a pane. Will not close an unsaved tab.",
3706                // todo(settings_ui): The default for this value is null and it's use in code
3707                // is complex, so I'm going to come back to this later
3708                field: Box::new(
3709                    SettingField {
3710                        json_path: Some("max_tabs"),
3711                        pick: |settings_content| settings_content.workspace.max_tabs.as_ref(),
3712                        write: |settings_content, value| {
3713                            settings_content.workspace.max_tabs = value;
3714                        },
3715                    }
3716                    .unimplemented(),
3717                ),
3718                metadata: None,
3719            }),
3720            SettingsPageItem::SettingItem(SettingItem {
3721                title: "Show Navigation History Buttons",
3722                description: "Show the navigation history buttons in the tab bar.",
3723                field: Box::new(SettingField {
3724                    json_path: Some("tab_bar.show_nav_history_buttons"),
3725                    pick: |settings_content| {
3726                        settings_content
3727                            .tab_bar
3728                            .as_ref()?
3729                            .show_nav_history_buttons
3730                            .as_ref()
3731                    },
3732                    write: |settings_content, value| {
3733                        settings_content
3734                            .tab_bar
3735                            .get_or_insert_default()
3736                            .show_nav_history_buttons = value;
3737                    },
3738                }),
3739                metadata: None,
3740                files: USER,
3741            }),
3742            SettingsPageItem::SettingItem(SettingItem {
3743                title: "Show Tab Bar Buttons",
3744                description: "Show the tab bar buttons (New, Split Pane, Zoom).",
3745                field: Box::new(SettingField {
3746                    json_path: Some("tab_bar.show_tab_bar_buttons"),
3747                    pick: |settings_content| {
3748                        settings_content
3749                            .tab_bar
3750                            .as_ref()?
3751                            .show_tab_bar_buttons
3752                            .as_ref()
3753                    },
3754                    write: |settings_content, value| {
3755                        settings_content
3756                            .tab_bar
3757                            .get_or_insert_default()
3758                            .show_tab_bar_buttons = value;
3759                    },
3760                }),
3761                metadata: None,
3762                files: USER,
3763            }),
3764            SettingsPageItem::SettingItem(SettingItem {
3765                title: "Pinned Tabs Layout",
3766                description: "Show pinned tabs in a separate row above unpinned tabs.",
3767                field: Box::new(SettingField {
3768                    json_path: Some("tab_bar.show_pinned_tabs_in_separate_row"),
3769                    pick: |settings_content| {
3770                        settings_content
3771                            .tab_bar
3772                            .as_ref()?
3773                            .show_pinned_tabs_in_separate_row
3774                            .as_ref()
3775                    },
3776                    write: |settings_content, value| {
3777                        settings_content
3778                            .tab_bar
3779                            .get_or_insert_default()
3780                            .show_pinned_tabs_in_separate_row = value;
3781                    },
3782                }),
3783                metadata: None,
3784                files: USER,
3785            }),
3786        ]
3787    }
3788
3789    fn tab_settings_section() -> [SettingsPageItem; 4] {
3790        [
3791            SettingsPageItem::SectionHeader("Tab Settings"),
3792            SettingsPageItem::SettingItem(SettingItem {
3793                title: "Activate On Close",
3794                description: "What to do after closing the current tab.",
3795                field: Box::new(SettingField {
3796                    json_path: Some("tabs.activate_on_close"),
3797                    pick: |settings_content| {
3798                        settings_content.tabs.as_ref()?.activate_on_close.as_ref()
3799                    },
3800                    write: |settings_content, value| {
3801                        settings_content
3802                            .tabs
3803                            .get_or_insert_default()
3804                            .activate_on_close = value;
3805                    },
3806                }),
3807                metadata: None,
3808                files: USER,
3809            }),
3810            SettingsPageItem::SettingItem(SettingItem {
3811                title: "Tab Show Diagnostics",
3812                description: "Which files containing diagnostic errors/warnings to mark in the tabs.",
3813                field: Box::new(SettingField {
3814                    json_path: Some("tabs.show_diagnostics"),
3815                    pick: |settings_content| {
3816                        settings_content.tabs.as_ref()?.show_diagnostics.as_ref()
3817                    },
3818                    write: |settings_content, value| {
3819                        settings_content
3820                            .tabs
3821                            .get_or_insert_default()
3822                            .show_diagnostics = value;
3823                    },
3824                }),
3825                metadata: None,
3826                files: USER,
3827            }),
3828            SettingsPageItem::SettingItem(SettingItem {
3829                title: "Show Close Button",
3830                description: "Controls the appearance behavior of the tab's close button.",
3831                field: Box::new(SettingField {
3832                    json_path: Some("tabs.show_close_button"),
3833                    pick: |settings_content| {
3834                        settings_content.tabs.as_ref()?.show_close_button.as_ref()
3835                    },
3836                    write: |settings_content, value| {
3837                        settings_content
3838                            .tabs
3839                            .get_or_insert_default()
3840                            .show_close_button = value;
3841                    },
3842                }),
3843                metadata: None,
3844                files: USER,
3845            }),
3846        ]
3847    }
3848
3849    fn preview_tabs_section() -> [SettingsPageItem; 8] {
3850        [
3851            SettingsPageItem::SectionHeader("Preview Tabs"),
3852            SettingsPageItem::SettingItem(SettingItem {
3853                title: "Preview Tabs Enabled",
3854                description: "Show opened editors as preview tabs.",
3855                field: Box::new(SettingField {
3856                    json_path: Some("preview_tabs.enabled"),
3857                    pick: |settings_content| {
3858                        settings_content.preview_tabs.as_ref()?.enabled.as_ref()
3859                    },
3860                    write: |settings_content, value| {
3861                        settings_content
3862                            .preview_tabs
3863                            .get_or_insert_default()
3864                            .enabled = value;
3865                    },
3866                }),
3867                metadata: None,
3868                files: USER,
3869            }),
3870            SettingsPageItem::SettingItem(SettingItem {
3871                title: "Enable Preview From Project Panel",
3872                description: "Whether to open tabs in preview mode when opened from the project panel with a single click.",
3873                field: Box::new(SettingField {
3874                    json_path: Some("preview_tabs.enable_preview_from_project_panel"),
3875                    pick: |settings_content| {
3876                        settings_content
3877                            .preview_tabs
3878                            .as_ref()?
3879                            .enable_preview_from_project_panel
3880                            .as_ref()
3881                    },
3882                    write: |settings_content, value| {
3883                        settings_content
3884                            .preview_tabs
3885                            .get_or_insert_default()
3886                            .enable_preview_from_project_panel = value;
3887                    },
3888                }),
3889                metadata: None,
3890                files: USER,
3891            }),
3892            SettingsPageItem::SettingItem(SettingItem {
3893                title: "Enable Preview From File Finder",
3894                description: "Whether to open tabs in preview mode when selected from the file finder.",
3895                field: Box::new(SettingField {
3896                    json_path: Some("preview_tabs.enable_preview_from_file_finder"),
3897                    pick: |settings_content| {
3898                        settings_content
3899                            .preview_tabs
3900                            .as_ref()?
3901                            .enable_preview_from_file_finder
3902                            .as_ref()
3903                    },
3904                    write: |settings_content, value| {
3905                        settings_content
3906                            .preview_tabs
3907                            .get_or_insert_default()
3908                            .enable_preview_from_file_finder = value;
3909                    },
3910                }),
3911                metadata: None,
3912                files: USER,
3913            }),
3914            SettingsPageItem::SettingItem(SettingItem {
3915                title: "Enable Preview From Multibuffer",
3916                description: "Whether to open tabs in preview mode when opened from a multibuffer.",
3917                field: Box::new(SettingField {
3918                    json_path: Some("preview_tabs.enable_preview_from_multibuffer"),
3919                    pick: |settings_content| {
3920                        settings_content
3921                            .preview_tabs
3922                            .as_ref()?
3923                            .enable_preview_from_multibuffer
3924                            .as_ref()
3925                    },
3926                    write: |settings_content, value| {
3927                        settings_content
3928                            .preview_tabs
3929                            .get_or_insert_default()
3930                            .enable_preview_from_multibuffer = value;
3931                    },
3932                }),
3933                metadata: None,
3934                files: USER,
3935            }),
3936            SettingsPageItem::SettingItem(SettingItem {
3937                title: "Enable Preview Multibuffer From Code Navigation",
3938                description: "Whether to open tabs in preview mode when code navigation is used to open a multibuffer.",
3939                field: Box::new(SettingField {
3940                    json_path: Some("preview_tabs.enable_preview_multibuffer_from_code_navigation"),
3941                    pick: |settings_content| {
3942                        settings_content
3943                            .preview_tabs
3944                            .as_ref()?
3945                            .enable_preview_multibuffer_from_code_navigation
3946                            .as_ref()
3947                    },
3948                    write: |settings_content, value| {
3949                        settings_content
3950                            .preview_tabs
3951                            .get_or_insert_default()
3952                            .enable_preview_multibuffer_from_code_navigation = value;
3953                    },
3954                }),
3955                metadata: None,
3956                files: USER,
3957            }),
3958            SettingsPageItem::SettingItem(SettingItem {
3959                title: "Enable Preview File From Code Navigation",
3960                description: "Whether to open tabs in preview mode when code navigation is used to open a single file.",
3961                field: Box::new(SettingField {
3962                    json_path: Some("preview_tabs.enable_preview_file_from_code_navigation"),
3963                    pick: |settings_content| {
3964                        settings_content
3965                            .preview_tabs
3966                            .as_ref()?
3967                            .enable_preview_file_from_code_navigation
3968                            .as_ref()
3969                    },
3970                    write: |settings_content, value| {
3971                        settings_content
3972                            .preview_tabs
3973                            .get_or_insert_default()
3974                            .enable_preview_file_from_code_navigation = value;
3975                    },
3976                }),
3977                metadata: None,
3978                files: USER,
3979            }),
3980            SettingsPageItem::SettingItem(SettingItem {
3981                title: "Enable Keep Preview On Code Navigation",
3982                description: "Whether to keep tabs in preview mode when code navigation is used to navigate away from them. If `enable_preview_file_from_code_navigation` or `enable_preview_multibuffer_from_code_navigation` is also true, the new tab may replace the existing one.",
3983                field: Box::new(SettingField {
3984                    json_path: Some("preview_tabs.enable_keep_preview_on_code_navigation"),
3985                    pick: |settings_content| {
3986                        settings_content
3987                            .preview_tabs
3988                            .as_ref()?
3989                            .enable_keep_preview_on_code_navigation
3990                            .as_ref()
3991                    },
3992                    write: |settings_content, value| {
3993                        settings_content
3994                            .preview_tabs
3995                            .get_or_insert_default()
3996                            .enable_keep_preview_on_code_navigation = value;
3997                    },
3998                }),
3999                metadata: None,
4000                files: USER,
4001            }),
4002        ]
4003    }
4004
4005    fn layout_section() -> [SettingsPageItem; 4] {
4006        [
4007            SettingsPageItem::SectionHeader("Layout"),
4008            SettingsPageItem::SettingItem(SettingItem {
4009                title: "Bottom Dock Layout",
4010                description: "Layout mode for the bottom dock.",
4011                field: Box::new(SettingField {
4012                    json_path: Some("bottom_dock_layout"),
4013                    pick: |settings_content| settings_content.workspace.bottom_dock_layout.as_ref(),
4014                    write: |settings_content, value| {
4015                        settings_content.workspace.bottom_dock_layout = value;
4016                    },
4017                }),
4018                metadata: None,
4019                files: USER,
4020            }),
4021            SettingsPageItem::SettingItem(SettingItem {
4022                files: USER,
4023                title: "Centered Layout Left Padding",
4024                description: "Left padding for centered layout.",
4025                field: Box::new(SettingField {
4026                    json_path: Some("centered_layout.left_padding"),
4027                    pick: |settings_content| {
4028                        settings_content
4029                            .workspace
4030                            .centered_layout
4031                            .as_ref()?
4032                            .left_padding
4033                            .as_ref()
4034                    },
4035                    write: |settings_content, value| {
4036                        settings_content
4037                            .workspace
4038                            .centered_layout
4039                            .get_or_insert_default()
4040                            .left_padding = value;
4041                    },
4042                }),
4043                metadata: None,
4044            }),
4045            SettingsPageItem::SettingItem(SettingItem {
4046                files: USER,
4047                title: "Centered Layout Right Padding",
4048                description: "Right padding for centered layout.",
4049                field: Box::new(SettingField {
4050                    json_path: Some("centered_layout.right_padding"),
4051                    pick: |settings_content| {
4052                        settings_content
4053                            .workspace
4054                            .centered_layout
4055                            .as_ref()?
4056                            .right_padding
4057                            .as_ref()
4058                    },
4059                    write: |settings_content, value| {
4060                        settings_content
4061                            .workspace
4062                            .centered_layout
4063                            .get_or_insert_default()
4064                            .right_padding = value;
4065                    },
4066                }),
4067                metadata: None,
4068            }),
4069        ]
4070    }
4071
4072    fn window_section() -> [SettingsPageItem; 3] {
4073        [
4074            SettingsPageItem::SectionHeader("Window"),
4075            // todo(settings_ui): Should we filter by platform.as_ref()?
4076            SettingsPageItem::SettingItem(SettingItem {
4077                title: "Use System Window Tabs",
4078                description: "(macOS only) whether to allow Windows to tab together.",
4079                field: Box::new(SettingField {
4080                    json_path: Some("use_system_window_tabs"),
4081                    pick: |settings_content| {
4082                        settings_content.workspace.use_system_window_tabs.as_ref()
4083                    },
4084                    write: |settings_content, value| {
4085                        settings_content.workspace.use_system_window_tabs = value;
4086                    },
4087                }),
4088                metadata: None,
4089                files: USER,
4090            }),
4091            SettingsPageItem::SettingItem(SettingItem {
4092                title: "Window Decorations",
4093                description: "(Linux only) whether Zed or your compositor should draw window decorations.",
4094                field: Box::new(SettingField {
4095                    json_path: Some("window_decorations"),
4096                    pick: |settings_content| settings_content.workspace.window_decorations.as_ref(),
4097                    write: |settings_content, value| {
4098                        settings_content.workspace.window_decorations = value;
4099                    },
4100                }),
4101                metadata: None,
4102                files: USER,
4103            }),
4104        ]
4105    }
4106
4107    fn pane_modifiers_section() -> [SettingsPageItem; 4] {
4108        [
4109            SettingsPageItem::SectionHeader("Pane Modifiers"),
4110            SettingsPageItem::SettingItem(SettingItem {
4111                title: "Inactive Opacity",
4112                description: "Opacity of inactive panels (0.0 - 1.0).",
4113                field: Box::new(SettingField {
4114                    json_path: Some("active_pane_modifiers.inactive_opacity"),
4115                    pick: |settings_content| {
4116                        settings_content
4117                            .workspace
4118                            .active_pane_modifiers
4119                            .as_ref()?
4120                            .inactive_opacity
4121                            .as_ref()
4122                    },
4123                    write: |settings_content, value| {
4124                        settings_content
4125                            .workspace
4126                            .active_pane_modifiers
4127                            .get_or_insert_default()
4128                            .inactive_opacity = value;
4129                    },
4130                }),
4131                metadata: None,
4132                files: USER,
4133            }),
4134            SettingsPageItem::SettingItem(SettingItem {
4135                title: "Border Size",
4136                description: "Size of the border surrounding the active pane.",
4137                field: Box::new(SettingField {
4138                    json_path: Some("active_pane_modifiers.border_size"),
4139                    pick: |settings_content| {
4140                        settings_content
4141                            .workspace
4142                            .active_pane_modifiers
4143                            .as_ref()?
4144                            .border_size
4145                            .as_ref()
4146                    },
4147                    write: |settings_content, value| {
4148                        settings_content
4149                            .workspace
4150                            .active_pane_modifiers
4151                            .get_or_insert_default()
4152                            .border_size = value;
4153                    },
4154                }),
4155                metadata: None,
4156                files: USER,
4157            }),
4158            SettingsPageItem::SettingItem(SettingItem {
4159                title: "Zoomed Padding",
4160                description: "Show padding for zoomed panes.",
4161                field: Box::new(SettingField {
4162                    json_path: Some("zoomed_padding"),
4163                    pick: |settings_content| settings_content.workspace.zoomed_padding.as_ref(),
4164                    write: |settings_content, value| {
4165                        settings_content.workspace.zoomed_padding = value;
4166                    },
4167                }),
4168                metadata: None,
4169                files: USER,
4170            }),
4171        ]
4172    }
4173
4174    fn pane_split_direction_section() -> [SettingsPageItem; 3] {
4175        [
4176            SettingsPageItem::SectionHeader("Pane Split Direction"),
4177            SettingsPageItem::SettingItem(SettingItem {
4178                title: "Vertical Split Direction",
4179                description: "Direction to split vertically.",
4180                field: Box::new(SettingField {
4181                    json_path: Some("pane_split_direction_vertical"),
4182                    pick: |settings_content| {
4183                        settings_content
4184                            .workspace
4185                            .pane_split_direction_vertical
4186                            .as_ref()
4187                    },
4188                    write: |settings_content, value| {
4189                        settings_content.workspace.pane_split_direction_vertical = value;
4190                    },
4191                }),
4192                metadata: None,
4193                files: USER,
4194            }),
4195            SettingsPageItem::SettingItem(SettingItem {
4196                title: "Horizontal Split Direction",
4197                description: "Direction to split horizontally.",
4198                field: Box::new(SettingField {
4199                    json_path: Some("pane_split_direction_horizontal"),
4200                    pick: |settings_content| {
4201                        settings_content
4202                            .workspace
4203                            .pane_split_direction_horizontal
4204                            .as_ref()
4205                    },
4206                    write: |settings_content, value| {
4207                        settings_content.workspace.pane_split_direction_horizontal = value;
4208                    },
4209                }),
4210                metadata: None,
4211                files: USER,
4212            }),
4213        ]
4214    }
4215
4216    SettingsPage {
4217        title: "Window & Layout",
4218        items: concat_sections![
4219            status_bar_section(),
4220            title_bar_section(),
4221            tab_bar_section(),
4222            tab_settings_section(),
4223            preview_tabs_section(),
4224            layout_section(),
4225            window_section(),
4226            pane_modifiers_section(),
4227            pane_split_direction_section(),
4228        ],
4229    }
4230}
4231
4232fn panels_page() -> SettingsPage {
4233    fn project_panel_section() -> [SettingsPageItem; 21] {
4234        [
4235            SettingsPageItem::SectionHeader("Project Panel"),
4236            SettingsPageItem::SettingItem(SettingItem {
4237                title: "Project Panel Dock",
4238                description: "Where to dock the project panel.",
4239                field: Box::new(SettingField {
4240                    json_path: Some("project_panel.dock"),
4241                    pick: |settings_content| settings_content.project_panel.as_ref()?.dock.as_ref(),
4242                    write: |settings_content, value| {
4243                        settings_content.project_panel.get_or_insert_default().dock = value;
4244                    },
4245                }),
4246                metadata: None,
4247                files: USER,
4248            }),
4249            SettingsPageItem::SettingItem(SettingItem {
4250                title: "Project Panel Default Width",
4251                description: "Default width of the project panel in pixels.",
4252                field: Box::new(SettingField {
4253                    json_path: Some("project_panel.default_width"),
4254                    pick: |settings_content| {
4255                        settings_content
4256                            .project_panel
4257                            .as_ref()?
4258                            .default_width
4259                            .as_ref()
4260                    },
4261                    write: |settings_content, value| {
4262                        settings_content
4263                            .project_panel
4264                            .get_or_insert_default()
4265                            .default_width = value;
4266                    },
4267                }),
4268                metadata: None,
4269                files: USER,
4270            }),
4271            SettingsPageItem::SettingItem(SettingItem {
4272                title: "Hide .gitignore",
4273                description: "Whether to hide the gitignore entries in the project panel.",
4274                field: Box::new(SettingField {
4275                    json_path: Some("project_panel.hide_gitignore"),
4276                    pick: |settings_content| {
4277                        settings_content
4278                            .project_panel
4279                            .as_ref()?
4280                            .hide_gitignore
4281                            .as_ref()
4282                    },
4283                    write: |settings_content, value| {
4284                        settings_content
4285                            .project_panel
4286                            .get_or_insert_default()
4287                            .hide_gitignore = value;
4288                    },
4289                }),
4290                metadata: None,
4291                files: USER,
4292            }),
4293            SettingsPageItem::SettingItem(SettingItem {
4294                title: "Entry Spacing",
4295                description: "Spacing between worktree entries in the project panel.",
4296                field: Box::new(SettingField {
4297                    json_path: Some("project_panel.entry_spacing"),
4298                    pick: |settings_content| {
4299                        settings_content
4300                            .project_panel
4301                            .as_ref()?
4302                            .entry_spacing
4303                            .as_ref()
4304                    },
4305                    write: |settings_content, value| {
4306                        settings_content
4307                            .project_panel
4308                            .get_or_insert_default()
4309                            .entry_spacing = value;
4310                    },
4311                }),
4312                metadata: None,
4313                files: USER,
4314            }),
4315            SettingsPageItem::SettingItem(SettingItem {
4316                title: "File Icons",
4317                description: "Show file icons in the project panel.",
4318                field: Box::new(SettingField {
4319                    json_path: Some("project_panel.file_icons"),
4320                    pick: |settings_content| {
4321                        settings_content.project_panel.as_ref()?.file_icons.as_ref()
4322                    },
4323                    write: |settings_content, value| {
4324                        settings_content
4325                            .project_panel
4326                            .get_or_insert_default()
4327                            .file_icons = value;
4328                    },
4329                }),
4330                metadata: None,
4331                files: USER,
4332            }),
4333            SettingsPageItem::SettingItem(SettingItem {
4334                title: "Folder Icons",
4335                description: "Whether to show folder icons or chevrons for directories in the project panel.",
4336                field: Box::new(SettingField {
4337                    json_path: Some("project_panel.folder_icons"),
4338                    pick: |settings_content| {
4339                        settings_content
4340                            .project_panel
4341                            .as_ref()?
4342                            .folder_icons
4343                            .as_ref()
4344                    },
4345                    write: |settings_content, value| {
4346                        settings_content
4347                            .project_panel
4348                            .get_or_insert_default()
4349                            .folder_icons = value;
4350                    },
4351                }),
4352                metadata: None,
4353                files: USER,
4354            }),
4355            SettingsPageItem::SettingItem(SettingItem {
4356                title: "Git Status",
4357                description: "Show the Git status in the project panel.",
4358                field: Box::new(SettingField {
4359                    json_path: Some("project_panel.git_status"),
4360                    pick: |settings_content| {
4361                        settings_content.project_panel.as_ref()?.git_status.as_ref()
4362                    },
4363                    write: |settings_content, value| {
4364                        settings_content
4365                            .project_panel
4366                            .get_or_insert_default()
4367                            .git_status = value;
4368                    },
4369                }),
4370                metadata: None,
4371                files: USER,
4372            }),
4373            SettingsPageItem::SettingItem(SettingItem {
4374                title: "Indent Size",
4375                description: "Amount of indentation for nested items.",
4376                field: Box::new(SettingField {
4377                    json_path: Some("project_panel.indent_size"),
4378                    pick: |settings_content| {
4379                        settings_content
4380                            .project_panel
4381                            .as_ref()?
4382                            .indent_size
4383                            .as_ref()
4384                    },
4385                    write: |settings_content, value| {
4386                        settings_content
4387                            .project_panel
4388                            .get_or_insert_default()
4389                            .indent_size = value;
4390                    },
4391                }),
4392                metadata: None,
4393                files: USER,
4394            }),
4395            SettingsPageItem::SettingItem(SettingItem {
4396                title: "Auto Reveal Entries",
4397                description: "Whether to reveal entries in the project panel automatically when a corresponding project entry becomes active.",
4398                field: Box::new(SettingField {
4399                    json_path: Some("project_panel.auto_reveal_entries"),
4400                    pick: |settings_content| {
4401                        settings_content
4402                            .project_panel
4403                            .as_ref()?
4404                            .auto_reveal_entries
4405                            .as_ref()
4406                    },
4407                    write: |settings_content, value| {
4408                        settings_content
4409                            .project_panel
4410                            .get_or_insert_default()
4411                            .auto_reveal_entries = value;
4412                    },
4413                }),
4414                metadata: None,
4415                files: USER,
4416            }),
4417            SettingsPageItem::SettingItem(SettingItem {
4418                title: "Starts Open",
4419                description: "Whether the project panel should open on startup.",
4420                field: Box::new(SettingField {
4421                    json_path: Some("project_panel.starts_open"),
4422                    pick: |settings_content| {
4423                        settings_content
4424                            .project_panel
4425                            .as_ref()?
4426                            .starts_open
4427                            .as_ref()
4428                    },
4429                    write: |settings_content, value| {
4430                        settings_content
4431                            .project_panel
4432                            .get_or_insert_default()
4433                            .starts_open = value;
4434                    },
4435                }),
4436                metadata: None,
4437                files: USER,
4438            }),
4439            SettingsPageItem::SettingItem(SettingItem {
4440                title: "Auto Fold Directories",
4441                description: "Whether to fold directories automatically and show compact folders when a directory has only one subdirectory inside.",
4442                field: Box::new(SettingField {
4443                    json_path: Some("project_panel.auto_fold_dirs"),
4444                    pick: |settings_content| {
4445                        settings_content
4446                            .project_panel
4447                            .as_ref()?
4448                            .auto_fold_dirs
4449                            .as_ref()
4450                    },
4451                    write: |settings_content, value| {
4452                        settings_content
4453                            .project_panel
4454                            .get_or_insert_default()
4455                            .auto_fold_dirs = value;
4456                    },
4457                }),
4458                metadata: None,
4459                files: USER,
4460            }),
4461            SettingsPageItem::SettingItem(SettingItem {
4462                title: "Bold Folder Labels",
4463                description: "Whether to show folder names with bold text in the project panel.",
4464                field: Box::new(SettingField {
4465                    json_path: Some("project_panel.bold_folder_labels"),
4466                    pick: |settings_content| {
4467                        settings_content
4468                            .project_panel
4469                            .as_ref()?
4470                            .bold_folder_labels
4471                            .as_ref()
4472                    },
4473                    write: |settings_content, value| {
4474                        settings_content
4475                            .project_panel
4476                            .get_or_insert_default()
4477                            .bold_folder_labels = value;
4478                    },
4479                }),
4480                metadata: None,
4481                files: USER,
4482            }),
4483            SettingsPageItem::SettingItem(SettingItem {
4484                title: "Show Scrollbar",
4485                description: "Show the scrollbar in the project panel.",
4486                field: Box::new(SettingField {
4487                    json_path: Some("project_panel.scrollbar.show"),
4488                    pick: |settings_content| {
4489                        show_scrollbar_or_editor(settings_content, |settings_content| {
4490                            settings_content
4491                                .project_panel
4492                                .as_ref()?
4493                                .scrollbar
4494                                .as_ref()?
4495                                .show
4496                                .as_ref()
4497                        })
4498                    },
4499                    write: |settings_content, value| {
4500                        settings_content
4501                            .project_panel
4502                            .get_or_insert_default()
4503                            .scrollbar
4504                            .get_or_insert_default()
4505                            .show = value;
4506                    },
4507                }),
4508                metadata: None,
4509                files: USER,
4510            }),
4511            SettingsPageItem::SettingItem(SettingItem {
4512                title: "Show Diagnostics",
4513                description: "Which files containing diagnostic errors/warnings to mark in the project panel.",
4514                field: Box::new(SettingField {
4515                    json_path: Some("project_panel.show_diagnostics"),
4516                    pick: |settings_content| {
4517                        settings_content
4518                            .project_panel
4519                            .as_ref()?
4520                            .show_diagnostics
4521                            .as_ref()
4522                    },
4523                    write: |settings_content, value| {
4524                        settings_content
4525                            .project_panel
4526                            .get_or_insert_default()
4527                            .show_diagnostics = value;
4528                    },
4529                }),
4530                metadata: None,
4531                files: USER,
4532            }),
4533            SettingsPageItem::SettingItem(SettingItem {
4534                title: "Sticky Scroll",
4535                description: "Whether to stick parent directories at top of the project panel.",
4536                field: Box::new(SettingField {
4537                    json_path: Some("project_panel.sticky_scroll"),
4538                    pick: |settings_content| {
4539                        settings_content
4540                            .project_panel
4541                            .as_ref()?
4542                            .sticky_scroll
4543                            .as_ref()
4544                    },
4545                    write: |settings_content, value| {
4546                        settings_content
4547                            .project_panel
4548                            .get_or_insert_default()
4549                            .sticky_scroll = value;
4550                    },
4551                }),
4552                metadata: None,
4553                files: USER,
4554            }),
4555            SettingsPageItem::SettingItem(SettingItem {
4556                files: USER,
4557                title: "Show Indent Guides",
4558                description: "Show indent guides in the project panel.",
4559                field: Box::new(SettingField {
4560                    json_path: Some("project_panel.indent_guides.show"),
4561                    pick: |settings_content| {
4562                        settings_content
4563                            .project_panel
4564                            .as_ref()?
4565                            .indent_guides
4566                            .as_ref()?
4567                            .show
4568                            .as_ref()
4569                    },
4570                    write: |settings_content, value| {
4571                        settings_content
4572                            .project_panel
4573                            .get_or_insert_default()
4574                            .indent_guides
4575                            .get_or_insert_default()
4576                            .show = value;
4577                    },
4578                }),
4579                metadata: None,
4580            }),
4581            SettingsPageItem::SettingItem(SettingItem {
4582                title: "Drag and Drop",
4583                description: "Whether to enable drag-and-drop operations in the project panel.",
4584                field: Box::new(SettingField {
4585                    json_path: Some("project_panel.drag_and_drop"),
4586                    pick: |settings_content| {
4587                        settings_content
4588                            .project_panel
4589                            .as_ref()?
4590                            .drag_and_drop
4591                            .as_ref()
4592                    },
4593                    write: |settings_content, value| {
4594                        settings_content
4595                            .project_panel
4596                            .get_or_insert_default()
4597                            .drag_and_drop = value;
4598                    },
4599                }),
4600                metadata: None,
4601                files: USER,
4602            }),
4603            SettingsPageItem::SettingItem(SettingItem {
4604                title: "Hide Root",
4605                description: "Whether to hide the root entry when only one folder is open in the window.",
4606                field: Box::new(SettingField {
4607                    json_path: Some("project_panel.drag_and_drop"),
4608                    pick: |settings_content| {
4609                        settings_content.project_panel.as_ref()?.hide_root.as_ref()
4610                    },
4611                    write: |settings_content, value| {
4612                        settings_content
4613                            .project_panel
4614                            .get_or_insert_default()
4615                            .hide_root = value;
4616                    },
4617                }),
4618                metadata: None,
4619                files: USER,
4620            }),
4621            SettingsPageItem::SettingItem(SettingItem {
4622                title: "Hide Hidden",
4623                description: "Whether to hide the hidden entries in the project panel.",
4624                field: Box::new(SettingField {
4625                    json_path: Some("project_panel.hide_hidden"),
4626                    pick: |settings_content| {
4627                        settings_content
4628                            .project_panel
4629                            .as_ref()?
4630                            .hide_hidden
4631                            .as_ref()
4632                    },
4633                    write: |settings_content, value| {
4634                        settings_content
4635                            .project_panel
4636                            .get_or_insert_default()
4637                            .hide_hidden = value;
4638                    },
4639                }),
4640                metadata: None,
4641                files: USER,
4642            }),
4643            SettingsPageItem::SettingItem(SettingItem {
4644                title: "Hidden Files",
4645                description: "Globs to match files that will be considered \"hidden\" and can be hidden from the project panel.",
4646                field: Box::new(
4647                    SettingField {
4648                        json_path: Some("worktree.hidden_files"),
4649                        pick: |settings_content| {
4650                            settings_content.project.worktree.hidden_files.as_ref()
4651                        },
4652                        write: |settings_content, value| {
4653                            settings_content.project.worktree.hidden_files = value;
4654                        },
4655                    }
4656                    .unimplemented(),
4657                ),
4658                metadata: None,
4659                files: USER,
4660            }),
4661        ]
4662    }
4663
4664    fn auto_open_files_section() -> [SettingsPageItem; 5] {
4665        [
4666            SettingsPageItem::SectionHeader("Auto Open Files"),
4667            SettingsPageItem::SettingItem(SettingItem {
4668                title: "On Create",
4669                description: "Whether to automatically open newly created files in the editor.",
4670                field: Box::new(SettingField {
4671                    json_path: Some("project_panel.auto_open.on_create"),
4672                    pick: |settings_content| {
4673                        settings_content
4674                            .project_panel
4675                            .as_ref()?
4676                            .auto_open
4677                            .as_ref()?
4678                            .on_create
4679                            .as_ref()
4680                    },
4681                    write: |settings_content, value| {
4682                        settings_content
4683                            .project_panel
4684                            .get_or_insert_default()
4685                            .auto_open
4686                            .get_or_insert_default()
4687                            .on_create = value;
4688                    },
4689                }),
4690                metadata: None,
4691                files: USER,
4692            }),
4693            SettingsPageItem::SettingItem(SettingItem {
4694                title: "On Paste",
4695                description: "Whether to automatically open files after pasting or duplicating them.",
4696                field: Box::new(SettingField {
4697                    json_path: Some("project_panel.auto_open.on_paste"),
4698                    pick: |settings_content| {
4699                        settings_content
4700                            .project_panel
4701                            .as_ref()?
4702                            .auto_open
4703                            .as_ref()?
4704                            .on_paste
4705                            .as_ref()
4706                    },
4707                    write: |settings_content, value| {
4708                        settings_content
4709                            .project_panel
4710                            .get_or_insert_default()
4711                            .auto_open
4712                            .get_or_insert_default()
4713                            .on_paste = value;
4714                    },
4715                }),
4716                metadata: None,
4717                files: USER,
4718            }),
4719            SettingsPageItem::SettingItem(SettingItem {
4720                title: "On Drop",
4721                description: "Whether to automatically open files dropped from external sources.",
4722                field: Box::new(SettingField {
4723                    json_path: Some("project_panel.auto_open.on_drop"),
4724                    pick: |settings_content| {
4725                        settings_content
4726                            .project_panel
4727                            .as_ref()?
4728                            .auto_open
4729                            .as_ref()?
4730                            .on_drop
4731                            .as_ref()
4732                    },
4733                    write: |settings_content, value| {
4734                        settings_content
4735                            .project_panel
4736                            .get_or_insert_default()
4737                            .auto_open
4738                            .get_or_insert_default()
4739                            .on_drop = value;
4740                    },
4741                }),
4742                metadata: None,
4743                files: USER,
4744            }),
4745            SettingsPageItem::SettingItem(SettingItem {
4746                title: "Sort Mode",
4747                description: "Sort order for entries in the project panel.",
4748                field: Box::new(SettingField {
4749                    pick: |settings_content| {
4750                        settings_content.project_panel.as_ref()?.sort_mode.as_ref()
4751                    },
4752                    write: |settings_content, value| {
4753                        settings_content
4754                            .project_panel
4755                            .get_or_insert_default()
4756                            .sort_mode = value;
4757                    },
4758                    json_path: Some("project_panel.sort_mode"),
4759                }),
4760                metadata: None,
4761                files: USER,
4762            }),
4763        ]
4764    }
4765
4766    fn terminal_panel_section() -> [SettingsPageItem; 2] {
4767        [
4768            SettingsPageItem::SectionHeader("Terminal Panel"),
4769            SettingsPageItem::SettingItem(SettingItem {
4770                title: "Terminal Dock",
4771                description: "Where to dock the terminal panel.",
4772                field: Box::new(SettingField {
4773                    json_path: Some("terminal.dock"),
4774                    pick: |settings_content| settings_content.terminal.as_ref()?.dock.as_ref(),
4775                    write: |settings_content, value| {
4776                        settings_content.terminal.get_or_insert_default().dock = value;
4777                    },
4778                }),
4779                metadata: None,
4780                files: USER,
4781            }),
4782        ]
4783    }
4784
4785    fn outline_panel_section() -> [SettingsPageItem; 11] {
4786        [
4787            SettingsPageItem::SectionHeader("Outline Panel"),
4788            SettingsPageItem::SettingItem(SettingItem {
4789                title: "Outline Panel Button",
4790                description: "Show the outline panel button in the status bar.",
4791                field: Box::new(SettingField {
4792                    json_path: Some("outline_panel.button"),
4793                    pick: |settings_content| {
4794                        settings_content.outline_panel.as_ref()?.button.as_ref()
4795                    },
4796                    write: |settings_content, value| {
4797                        settings_content
4798                            .outline_panel
4799                            .get_or_insert_default()
4800                            .button = value;
4801                    },
4802                }),
4803                metadata: None,
4804                files: USER,
4805            }),
4806            SettingsPageItem::SettingItem(SettingItem {
4807                title: "Outline Panel Dock",
4808                description: "Where to dock the outline panel.",
4809                field: Box::new(SettingField {
4810                    json_path: Some("outline_panel.dock"),
4811                    pick: |settings_content| settings_content.outline_panel.as_ref()?.dock.as_ref(),
4812                    write: |settings_content, value| {
4813                        settings_content.outline_panel.get_or_insert_default().dock = value;
4814                    },
4815                }),
4816                metadata: None,
4817                files: USER,
4818            }),
4819            SettingsPageItem::SettingItem(SettingItem {
4820                title: "Outline Panel Default Width",
4821                description: "Default width of the outline panel in pixels.",
4822                field: Box::new(SettingField {
4823                    json_path: Some("outline_panel.default_width"),
4824                    pick: |settings_content| {
4825                        settings_content
4826                            .outline_panel
4827                            .as_ref()?
4828                            .default_width
4829                            .as_ref()
4830                    },
4831                    write: |settings_content, value| {
4832                        settings_content
4833                            .outline_panel
4834                            .get_or_insert_default()
4835                            .default_width = value;
4836                    },
4837                }),
4838                metadata: None,
4839                files: USER,
4840            }),
4841            SettingsPageItem::SettingItem(SettingItem {
4842                title: "File Icons",
4843                description: "Show file icons in the outline panel.",
4844                field: Box::new(SettingField {
4845                    json_path: Some("outline_panel.file_icons"),
4846                    pick: |settings_content| {
4847                        settings_content.outline_panel.as_ref()?.file_icons.as_ref()
4848                    },
4849                    write: |settings_content, value| {
4850                        settings_content
4851                            .outline_panel
4852                            .get_or_insert_default()
4853                            .file_icons = value;
4854                    },
4855                }),
4856                metadata: None,
4857                files: USER,
4858            }),
4859            SettingsPageItem::SettingItem(SettingItem {
4860                title: "Folder Icons",
4861                description: "Whether to show folder icons or chevrons for directories in the outline panel.",
4862                field: Box::new(SettingField {
4863                    json_path: Some("outline_panel.folder_icons"),
4864                    pick: |settings_content| {
4865                        settings_content
4866                            .outline_panel
4867                            .as_ref()?
4868                            .folder_icons
4869                            .as_ref()
4870                    },
4871                    write: |settings_content, value| {
4872                        settings_content
4873                            .outline_panel
4874                            .get_or_insert_default()
4875                            .folder_icons = value;
4876                    },
4877                }),
4878                metadata: None,
4879                files: USER,
4880            }),
4881            SettingsPageItem::SettingItem(SettingItem {
4882                title: "Git Status",
4883                description: "Show the Git status in the outline panel.",
4884                field: Box::new(SettingField {
4885                    json_path: Some("outline_panel.git_status"),
4886                    pick: |settings_content| {
4887                        settings_content.outline_panel.as_ref()?.git_status.as_ref()
4888                    },
4889                    write: |settings_content, value| {
4890                        settings_content
4891                            .outline_panel
4892                            .get_or_insert_default()
4893                            .git_status = value;
4894                    },
4895                }),
4896                metadata: None,
4897                files: USER,
4898            }),
4899            SettingsPageItem::SettingItem(SettingItem {
4900                title: "Indent Size",
4901                description: "Amount of indentation for nested items.",
4902                field: Box::new(SettingField {
4903                    json_path: Some("outline_panel.indent_size"),
4904                    pick: |settings_content| {
4905                        settings_content
4906                            .outline_panel
4907                            .as_ref()?
4908                            .indent_size
4909                            .as_ref()
4910                    },
4911                    write: |settings_content, value| {
4912                        settings_content
4913                            .outline_panel
4914                            .get_or_insert_default()
4915                            .indent_size = value;
4916                    },
4917                }),
4918                metadata: None,
4919                files: USER,
4920            }),
4921            SettingsPageItem::SettingItem(SettingItem {
4922                title: "Auto Reveal Entries",
4923                description: "Whether to reveal when a corresponding outline entry becomes active.",
4924                field: Box::new(SettingField {
4925                    json_path: Some("outline_panel.auto_reveal_entries"),
4926                    pick: |settings_content| {
4927                        settings_content
4928                            .outline_panel
4929                            .as_ref()?
4930                            .auto_reveal_entries
4931                            .as_ref()
4932                    },
4933                    write: |settings_content, value| {
4934                        settings_content
4935                            .outline_panel
4936                            .get_or_insert_default()
4937                            .auto_reveal_entries = value;
4938                    },
4939                }),
4940                metadata: None,
4941                files: USER,
4942            }),
4943            SettingsPageItem::SettingItem(SettingItem {
4944                title: "Auto Fold Directories",
4945                description: "Whether to fold directories automatically when a directory contains only one subdirectory.",
4946                field: Box::new(SettingField {
4947                    json_path: Some("outline_panel.auto_fold_dirs"),
4948                    pick: |settings_content| {
4949                        settings_content
4950                            .outline_panel
4951                            .as_ref()?
4952                            .auto_fold_dirs
4953                            .as_ref()
4954                    },
4955                    write: |settings_content, value| {
4956                        settings_content
4957                            .outline_panel
4958                            .get_or_insert_default()
4959                            .auto_fold_dirs = value;
4960                    },
4961                }),
4962                metadata: None,
4963                files: USER,
4964            }),
4965            SettingsPageItem::SettingItem(SettingItem {
4966                files: USER,
4967                title: "Show Indent Guides",
4968                description: "When to show indent guides in the outline panel.",
4969                field: Box::new(SettingField {
4970                    json_path: Some("outline_panel.indent_guides.show"),
4971                    pick: |settings_content| {
4972                        settings_content
4973                            .outline_panel
4974                            .as_ref()?
4975                            .indent_guides
4976                            .as_ref()?
4977                            .show
4978                            .as_ref()
4979                    },
4980                    write: |settings_content, value| {
4981                        settings_content
4982                            .outline_panel
4983                            .get_or_insert_default()
4984                            .indent_guides
4985                            .get_or_insert_default()
4986                            .show = value;
4987                    },
4988                }),
4989                metadata: None,
4990            }),
4991        ]
4992    }
4993
4994    fn git_panel_section() -> [SettingsPageItem; 10] {
4995        [
4996            SettingsPageItem::SectionHeader("Git Panel"),
4997            SettingsPageItem::SettingItem(SettingItem {
4998                title: "Git Panel Button",
4999                description: "Show the Git panel button in the status bar.",
5000                field: Box::new(SettingField {
5001                    json_path: Some("git_panel.button"),
5002                    pick: |settings_content| settings_content.git_panel.as_ref()?.button.as_ref(),
5003                    write: |settings_content, value| {
5004                        settings_content.git_panel.get_or_insert_default().button = value;
5005                    },
5006                }),
5007                metadata: None,
5008                files: USER,
5009            }),
5010            SettingsPageItem::SettingItem(SettingItem {
5011                title: "Git Panel Dock",
5012                description: "Where to dock the Git panel.",
5013                field: Box::new(SettingField {
5014                    json_path: Some("git_panel.dock"),
5015                    pick: |settings_content| settings_content.git_panel.as_ref()?.dock.as_ref(),
5016                    write: |settings_content, value| {
5017                        settings_content.git_panel.get_or_insert_default().dock = value;
5018                    },
5019                }),
5020                metadata: None,
5021                files: USER,
5022            }),
5023            SettingsPageItem::SettingItem(SettingItem {
5024                title: "Git Panel Default Width",
5025                description: "Default width of the Git panel in pixels.",
5026                field: Box::new(SettingField {
5027                    json_path: Some("git_panel.default_width"),
5028                    pick: |settings_content| {
5029                        settings_content.git_panel.as_ref()?.default_width.as_ref()
5030                    },
5031                    write: |settings_content, value| {
5032                        settings_content
5033                            .git_panel
5034                            .get_or_insert_default()
5035                            .default_width = value;
5036                    },
5037                }),
5038                metadata: None,
5039                files: USER,
5040            }),
5041            SettingsPageItem::SettingItem(SettingItem {
5042                title: "Git Panel Status Style",
5043                description: "How entry statuses are displayed.",
5044                field: Box::new(SettingField {
5045                    json_path: Some("git_panel.status_style"),
5046                    pick: |settings_content| {
5047                        settings_content.git_panel.as_ref()?.status_style.as_ref()
5048                    },
5049                    write: |settings_content, value| {
5050                        settings_content
5051                            .git_panel
5052                            .get_or_insert_default()
5053                            .status_style = value;
5054                    },
5055                }),
5056                metadata: None,
5057                files: USER,
5058            }),
5059            SettingsPageItem::SettingItem(SettingItem {
5060                title: "Fallback Branch Name",
5061                description: "Default branch name will be when init.defaultbranch is not set in Git.",
5062                field: Box::new(SettingField {
5063                    json_path: Some("git_panel.fallback_branch_name"),
5064                    pick: |settings_content| {
5065                        settings_content
5066                            .git_panel
5067                            .as_ref()?
5068                            .fallback_branch_name
5069                            .as_ref()
5070                    },
5071                    write: |settings_content, value| {
5072                        settings_content
5073                            .git_panel
5074                            .get_or_insert_default()
5075                            .fallback_branch_name = value;
5076                    },
5077                }),
5078                metadata: None,
5079                files: USER,
5080            }),
5081            SettingsPageItem::SettingItem(SettingItem {
5082                title: "Sort By Path",
5083                description: "Enable to sort entries in the panel by path, disable to sort by status.",
5084                field: Box::new(SettingField {
5085                    json_path: Some("git_panel.sort_by_path"),
5086                    pick: |settings_content| {
5087                        settings_content.git_panel.as_ref()?.sort_by_path.as_ref()
5088                    },
5089                    write: |settings_content, value| {
5090                        settings_content
5091                            .git_panel
5092                            .get_or_insert_default()
5093                            .sort_by_path = value;
5094                    },
5095                }),
5096                metadata: None,
5097                files: USER,
5098            }),
5099            SettingsPageItem::SettingItem(SettingItem {
5100                title: "Collapse Untracked Diff",
5101                description: "Whether to collapse untracked files in the diff panel.",
5102                field: Box::new(SettingField {
5103                    json_path: Some("git_panel.collapse_untracked_diff"),
5104                    pick: |settings_content| {
5105                        settings_content
5106                            .git_panel
5107                            .as_ref()?
5108                            .collapse_untracked_diff
5109                            .as_ref()
5110                    },
5111                    write: |settings_content, value| {
5112                        settings_content
5113                            .git_panel
5114                            .get_or_insert_default()
5115                            .collapse_untracked_diff = value;
5116                    },
5117                }),
5118                metadata: None,
5119                files: USER,
5120            }),
5121            SettingsPageItem::SettingItem(SettingItem {
5122                title: "Tree View",
5123                description: "Enable to show entries in tree view list, disable to show in flat view list.",
5124                field: Box::new(SettingField {
5125                    json_path: Some("git_panel.tree_view"),
5126                    pick: |settings_content| {
5127                        settings_content.git_panel.as_ref()?.tree_view.as_ref()
5128                    },
5129                    write: |settings_content, value| {
5130                        settings_content.git_panel.get_or_insert_default().tree_view = value;
5131                    },
5132                }),
5133                metadata: None,
5134                files: USER,
5135            }),
5136            SettingsPageItem::SettingItem(SettingItem {
5137                title: "Scroll Bar",
5138                description: "How and when the scrollbar should be displayed.",
5139                field: Box::new(SettingField {
5140                    json_path: Some("git_panel.scrollbar.show"),
5141                    pick: |settings_content| {
5142                        show_scrollbar_or_editor(settings_content, |settings_content| {
5143                            settings_content
5144                                .git_panel
5145                                .as_ref()?
5146                                .scrollbar
5147                                .as_ref()?
5148                                .show
5149                                .as_ref()
5150                        })
5151                    },
5152                    write: |settings_content, value| {
5153                        settings_content
5154                            .git_panel
5155                            .get_or_insert_default()
5156                            .scrollbar
5157                            .get_or_insert_default()
5158                            .show = value;
5159                    },
5160                }),
5161                metadata: None,
5162                files: USER,
5163            }),
5164        ]
5165    }
5166
5167    fn debugger_panel_section() -> [SettingsPageItem; 2] {
5168        [
5169            SettingsPageItem::SectionHeader("Debugger Panel"),
5170            SettingsPageItem::SettingItem(SettingItem {
5171                title: "Debugger Panel Dock",
5172                description: "The dock position of the debug panel.",
5173                field: Box::new(SettingField {
5174                    json_path: Some("debugger.dock"),
5175                    pick: |settings_content| settings_content.debugger.as_ref()?.dock.as_ref(),
5176                    write: |settings_content, value| {
5177                        settings_content.debugger.get_or_insert_default().dock = value;
5178                    },
5179                }),
5180                metadata: None,
5181                files: USER,
5182            }),
5183        ]
5184    }
5185
5186    fn notification_panel_section() -> [SettingsPageItem; 4] {
5187        [
5188            SettingsPageItem::SectionHeader("Notification Panel"),
5189            SettingsPageItem::SettingItem(SettingItem {
5190                title: "Notification Panel Button",
5191                description: "Show the notification panel button in the status bar.",
5192                field: Box::new(SettingField {
5193                    json_path: Some("notification_panel.button"),
5194                    pick: |settings_content| {
5195                        settings_content
5196                            .notification_panel
5197                            .as_ref()?
5198                            .button
5199                            .as_ref()
5200                    },
5201                    write: |settings_content, value| {
5202                        settings_content
5203                            .notification_panel
5204                            .get_or_insert_default()
5205                            .button = value;
5206                    },
5207                }),
5208                metadata: None,
5209                files: USER,
5210            }),
5211            SettingsPageItem::SettingItem(SettingItem {
5212                title: "Notification Panel Dock",
5213                description: "Where to dock the notification panel.",
5214                field: Box::new(SettingField {
5215                    json_path: Some("notification_panel.dock"),
5216                    pick: |settings_content| {
5217                        settings_content.notification_panel.as_ref()?.dock.as_ref()
5218                    },
5219                    write: |settings_content, value| {
5220                        settings_content
5221                            .notification_panel
5222                            .get_or_insert_default()
5223                            .dock = value;
5224                    },
5225                }),
5226                metadata: None,
5227                files: USER,
5228            }),
5229            SettingsPageItem::SettingItem(SettingItem {
5230                title: "Notification Panel Default Width",
5231                description: "Default width of the notification panel in pixels.",
5232                field: Box::new(SettingField {
5233                    json_path: Some("notification_panel.default_width"),
5234                    pick: |settings_content| {
5235                        settings_content
5236                            .notification_panel
5237                            .as_ref()?
5238                            .default_width
5239                            .as_ref()
5240                    },
5241                    write: |settings_content, value| {
5242                        settings_content
5243                            .notification_panel
5244                            .get_or_insert_default()
5245                            .default_width = value;
5246                    },
5247                }),
5248                metadata: None,
5249                files: USER,
5250            }),
5251        ]
5252    }
5253
5254    fn collaboration_panel_section() -> [SettingsPageItem; 4] {
5255        [
5256            SettingsPageItem::SectionHeader("Collaboration Panel"),
5257            SettingsPageItem::SettingItem(SettingItem {
5258                title: "Collaboration Panel Button",
5259                description: "Show the collaboration panel button in the status bar.",
5260                field: Box::new(SettingField {
5261                    json_path: Some("collaboration_panel.button"),
5262                    pick: |settings_content| {
5263                        settings_content
5264                            .collaboration_panel
5265                            .as_ref()?
5266                            .button
5267                            .as_ref()
5268                    },
5269                    write: |settings_content, value| {
5270                        settings_content
5271                            .collaboration_panel
5272                            .get_or_insert_default()
5273                            .button = value;
5274                    },
5275                }),
5276                metadata: None,
5277                files: USER,
5278            }),
5279            SettingsPageItem::SettingItem(SettingItem {
5280                title: "Collaboration Panel Dock",
5281                description: "Where to dock the collaboration panel.",
5282                field: Box::new(SettingField {
5283                    json_path: Some("collaboration_panel.dock"),
5284                    pick: |settings_content| {
5285                        settings_content.collaboration_panel.as_ref()?.dock.as_ref()
5286                    },
5287                    write: |settings_content, value| {
5288                        settings_content
5289                            .collaboration_panel
5290                            .get_or_insert_default()
5291                            .dock = value;
5292                    },
5293                }),
5294                metadata: None,
5295                files: USER,
5296            }),
5297            SettingsPageItem::SettingItem(SettingItem {
5298                title: "Collaboration Panel Default Width",
5299                description: "Default width of the collaboration panel in pixels.",
5300                field: Box::new(SettingField {
5301                    json_path: Some("collaboration_panel.dock"),
5302                    pick: |settings_content| {
5303                        settings_content
5304                            .collaboration_panel
5305                            .as_ref()?
5306                            .default_width
5307                            .as_ref()
5308                    },
5309                    write: |settings_content, value| {
5310                        settings_content
5311                            .collaboration_panel
5312                            .get_or_insert_default()
5313                            .default_width = value;
5314                    },
5315                }),
5316                metadata: None,
5317                files: USER,
5318            }),
5319        ]
5320    }
5321
5322    fn agent_panel_section() -> [SettingsPageItem; 5] {
5323        [
5324            SettingsPageItem::SectionHeader("Agent Panel"),
5325            SettingsPageItem::SettingItem(SettingItem {
5326                title: "Agent Panel Button",
5327                description: "Whether to show the agent panel button in the status bar.",
5328                field: Box::new(SettingField {
5329                    json_path: Some("agent.button"),
5330                    pick: |settings_content| settings_content.agent.as_ref()?.button.as_ref(),
5331                    write: |settings_content, value| {
5332                        settings_content.agent.get_or_insert_default().button = value;
5333                    },
5334                }),
5335                metadata: None,
5336                files: USER,
5337            }),
5338            SettingsPageItem::SettingItem(SettingItem {
5339                title: "Agent Panel Dock",
5340                description: "Where to dock the agent panel.",
5341                field: Box::new(SettingField {
5342                    json_path: Some("agent.dock"),
5343                    pick: |settings_content| settings_content.agent.as_ref()?.dock.as_ref(),
5344                    write: |settings_content, value| {
5345                        settings_content.agent.get_or_insert_default().dock = value;
5346                    },
5347                }),
5348                metadata: None,
5349                files: USER,
5350            }),
5351            SettingsPageItem::SettingItem(SettingItem {
5352                title: "Agent Panel Default Width",
5353                description: "Default width when the agent panel is docked to the left or right.",
5354                field: Box::new(SettingField {
5355                    json_path: Some("agent.default_width"),
5356                    pick: |settings_content| {
5357                        settings_content.agent.as_ref()?.default_width.as_ref()
5358                    },
5359                    write: |settings_content, value| {
5360                        settings_content.agent.get_or_insert_default().default_width = value;
5361                    },
5362                }),
5363                metadata: None,
5364                files: USER,
5365            }),
5366            SettingsPageItem::SettingItem(SettingItem {
5367                title: "Agent Panel Default Height",
5368                description: "Default height when the agent panel is docked to the bottom.",
5369                field: Box::new(SettingField {
5370                    json_path: Some("agent.default_height"),
5371                    pick: |settings_content| {
5372                        settings_content.agent.as_ref()?.default_height.as_ref()
5373                    },
5374                    write: |settings_content, value| {
5375                        settings_content
5376                            .agent
5377                            .get_or_insert_default()
5378                            .default_height = value;
5379                    },
5380                }),
5381                metadata: None,
5382                files: USER,
5383            }),
5384        ]
5385    }
5386
5387    SettingsPage {
5388        title: "Panels",
5389        items: concat_sections![
5390            project_panel_section(),
5391            auto_open_files_section(),
5392            terminal_panel_section(),
5393            outline_panel_section(),
5394            git_panel_section(),
5395            debugger_panel_section(),
5396            notification_panel_section(),
5397            collaboration_panel_section(),
5398            agent_panel_section(),
5399        ],
5400    }
5401}
5402
5403fn debugger_page() -> SettingsPage {
5404    fn general_section() -> [SettingsPageItem; 6] {
5405        [
5406            SettingsPageItem::SectionHeader("General"),
5407            SettingsPageItem::SettingItem(SettingItem {
5408                title: "Stepping Granularity",
5409                description: "Determines the stepping granularity for debug operations.",
5410                field: Box::new(SettingField {
5411                    json_path: Some("debugger.stepping_granularity"),
5412                    pick: |settings_content| {
5413                        settings_content
5414                            .debugger
5415                            .as_ref()?
5416                            .stepping_granularity
5417                            .as_ref()
5418                    },
5419                    write: |settings_content, value| {
5420                        settings_content
5421                            .debugger
5422                            .get_or_insert_default()
5423                            .stepping_granularity = value;
5424                    },
5425                }),
5426                metadata: None,
5427                files: USER,
5428            }),
5429            SettingsPageItem::SettingItem(SettingItem {
5430                title: "Save Breakpoints",
5431                description: "Whether breakpoints should be reused across Zed sessions.",
5432                field: Box::new(SettingField {
5433                    json_path: Some("debugger.save_breakpoints"),
5434                    pick: |settings_content| {
5435                        settings_content
5436                            .debugger
5437                            .as_ref()?
5438                            .save_breakpoints
5439                            .as_ref()
5440                    },
5441                    write: |settings_content, value| {
5442                        settings_content
5443                            .debugger
5444                            .get_or_insert_default()
5445                            .save_breakpoints = value;
5446                    },
5447                }),
5448                metadata: None,
5449                files: USER,
5450            }),
5451            SettingsPageItem::SettingItem(SettingItem {
5452                title: "Timeout",
5453                description: "Time in milliseconds until timeout error when connecting to a TCP debug adapter.",
5454                field: Box::new(SettingField {
5455                    json_path: Some("debugger.timeout"),
5456                    pick: |settings_content| settings_content.debugger.as_ref()?.timeout.as_ref(),
5457                    write: |settings_content, value| {
5458                        settings_content.debugger.get_or_insert_default().timeout = value;
5459                    },
5460                }),
5461                metadata: None,
5462                files: USER,
5463            }),
5464            SettingsPageItem::SettingItem(SettingItem {
5465                title: "Log DAP Communications",
5466                description: "Whether to log messages between active debug adapters and Zed.",
5467                field: Box::new(SettingField {
5468                    json_path: Some("debugger.log_dap_communications"),
5469                    pick: |settings_content| {
5470                        settings_content
5471                            .debugger
5472                            .as_ref()?
5473                            .log_dap_communications
5474                            .as_ref()
5475                    },
5476                    write: |settings_content, value| {
5477                        settings_content
5478                            .debugger
5479                            .get_or_insert_default()
5480                            .log_dap_communications = value;
5481                    },
5482                }),
5483                metadata: None,
5484                files: USER,
5485            }),
5486            SettingsPageItem::SettingItem(SettingItem {
5487                title: "Format DAP Log Messages",
5488                description: "Whether to format DAP messages when adding them to debug adapter logger.",
5489                field: Box::new(SettingField {
5490                    json_path: Some("debugger.format_dap_log_messages"),
5491                    pick: |settings_content| {
5492                        settings_content
5493                            .debugger
5494                            .as_ref()?
5495                            .format_dap_log_messages
5496                            .as_ref()
5497                    },
5498                    write: |settings_content, value| {
5499                        settings_content
5500                            .debugger
5501                            .get_or_insert_default()
5502                            .format_dap_log_messages = value;
5503                    },
5504                }),
5505                metadata: None,
5506                files: USER,
5507            }),
5508        ]
5509    }
5510
5511    SettingsPage {
5512        title: "Debugger",
5513        items: concat_sections![general_section()],
5514    }
5515}
5516
5517fn terminal_page() -> SettingsPage {
5518    fn environment_section() -> [SettingsPageItem; 5] {
5519        [
5520                SettingsPageItem::SectionHeader("Environment"),
5521                SettingsPageItem::DynamicItem(DynamicItem {
5522                    discriminant: SettingItem {
5523                        files: USER | PROJECT,
5524                        title: "Shell",
5525                        description: "What shell to use when opening a terminal.",
5526                        field: Box::new(SettingField {
5527                            json_path: Some("terminal.shell$"),
5528                            pick: |settings_content| {
5529                                Some(&dynamic_variants::<settings::Shell>()[
5530                                    settings_content
5531                                        .terminal
5532                                        .as_ref()?
5533                                        .project
5534                                        .shell
5535                                        .as_ref()?
5536                                        .discriminant() as usize
5537                                ])
5538                            },
5539                            write: |settings_content, value| {
5540                                let Some(value) = value else {
5541                                    if let Some(terminal) = settings_content.terminal.as_mut() {
5542                                        terminal.project.shell = None;
5543                                    }
5544                                    return;
5545                                };
5546                                let settings_value = settings_content
5547                                    .terminal
5548                                    .get_or_insert_default()
5549                                    .project
5550                                    .shell
5551                                    .get_or_insert_with(|| settings::Shell::default());
5552                                let default_shell = if cfg!(target_os = "windows") {
5553                                    "powershell.exe"
5554                                } else {
5555                                    "sh"
5556                                };
5557                                *settings_value = match value {
5558                                    settings::ShellDiscriminants::System => settings::Shell::System,
5559                                    settings::ShellDiscriminants::Program => {
5560                                        let program = match settings_value {
5561                                            settings::Shell::Program(program) => program.clone(),
5562                                            settings::Shell::WithArguments { program, .. } => program.clone(),
5563                                            _ => String::from(default_shell),
5564                                        };
5565                                        settings::Shell::Program(program)
5566                                    }
5567                                    settings::ShellDiscriminants::WithArguments => {
5568                                        let (program, args, title_override) = match settings_value {
5569                                            settings::Shell::Program(program) => (program.clone(), vec![], None),
5570                                            settings::Shell::WithArguments {
5571                                                program,
5572                                                args,
5573                                                title_override,
5574                                            } => (program.clone(), args.clone(), title_override.clone()),
5575                                            _ => (String::from(default_shell), vec![], None),
5576                                        };
5577                                        settings::Shell::WithArguments {
5578                                            program,
5579                                            args,
5580                                            title_override,
5581                                        }
5582                                    }
5583                                };
5584                            },
5585                        }),
5586                        metadata: None,
5587                    },
5588                    pick_discriminant: |settings_content| {
5589                        Some(
5590                            settings_content
5591                                .terminal
5592                                .as_ref()?
5593                                .project
5594                                .shell
5595                                .as_ref()?
5596                                .discriminant() as usize,
5597                        )
5598                    },
5599                    fields: dynamic_variants::<settings::Shell>()
5600                        .into_iter()
5601                        .map(|variant| match variant {
5602                            settings::ShellDiscriminants::System => vec![],
5603                            settings::ShellDiscriminants::Program => vec![SettingItem {
5604                                files: USER | PROJECT,
5605                                title: "Program",
5606                                description: "The shell program to use.",
5607                                field: Box::new(SettingField {
5608                                    json_path: Some("terminal.shell"),
5609                                    pick: |settings_content| match settings_content.terminal.as_ref()?.project.shell.as_ref()
5610                                    {
5611                                        Some(settings::Shell::Program(program)) => Some(program),
5612                                        _ => None,
5613                                    },
5614                                    write: |settings_content, value| {
5615                                        let Some(value) = value else {
5616                                            return;
5617                                        };
5618                                        match settings_content
5619                                            .terminal
5620                                            .get_or_insert_default()
5621                                            .project
5622                                            .shell
5623                                            .as_mut()
5624                                        {
5625                                            Some(settings::Shell::Program(program)) => *program = value,
5626                                            _ => return,
5627                                        }
5628                                    },
5629                                }),
5630                                metadata: None,
5631                            }],
5632                            settings::ShellDiscriminants::WithArguments => vec![
5633                                SettingItem {
5634                                    files: USER | PROJECT,
5635                                    title: "Program",
5636                                    description: "The shell program to run.",
5637                                    field: Box::new(SettingField {
5638                                        json_path: Some("terminal.shell.program"),
5639                                        pick: |settings_content| {
5640                                            match settings_content.terminal.as_ref()?.project.shell.as_ref() {
5641                                                Some(settings::Shell::WithArguments { program, .. }) => Some(program),
5642                                                _ => None,
5643                                            }
5644                                        },
5645                                        write: |settings_content, value| {
5646                                            let Some(value) = value else {
5647                                                return;
5648                                            };
5649                                            match settings_content
5650                                                .terminal
5651                                                .get_or_insert_default()
5652                                                .project
5653                                                .shell
5654                                                .as_mut()
5655                                            {
5656                                                Some(settings::Shell::WithArguments { program, .. }) => {
5657                                                    *program = value
5658                                                }
5659                                                _ => return,
5660                                            }
5661                                        },
5662                                    }),
5663                                    metadata: None,
5664                                },
5665                                SettingItem {
5666                                    files: USER | PROJECT,
5667                                    title: "Arguments",
5668                                    description: "The arguments to pass to the shell program.",
5669                                    field: Box::new(
5670                                        SettingField {
5671                                            json_path: Some("terminal.shell.args"),
5672                                            pick: |settings_content| {
5673                                                match settings_content.terminal.as_ref()?.project.shell.as_ref() {
5674                                                    Some(settings::Shell::WithArguments { args, .. }) => Some(args),
5675                                                    _ => None,
5676                                                }
5677                                            },
5678                                            write: |settings_content, value| {
5679                                                let Some(value) = value else {
5680                                                    return;
5681                                                };
5682                                                match settings_content
5683                                                    .terminal
5684                                                    .get_or_insert_default()
5685                                                    .project
5686                                                    .shell
5687                                                    .as_mut()
5688                                                {
5689                                                    Some(settings::Shell::WithArguments { args, .. }) => *args = value,
5690                                                    _ => return,
5691                                                }
5692                                            },
5693                                        }
5694                                        .unimplemented(),
5695                                    ),
5696                                    metadata: None,
5697                                },
5698                                SettingItem {
5699                                    files: USER | PROJECT,
5700                                    title: "Title Override",
5701                                    description: "An optional string to override the title of the terminal tab.",
5702                                    field: Box::new(SettingField {
5703                                        json_path: Some("terminal.shell.title_override"),
5704                                        pick: |settings_content| {
5705                                            match settings_content.terminal.as_ref()?.project.shell.as_ref() {
5706                                                Some(settings::Shell::WithArguments { title_override, .. }) => {
5707                                                    title_override.as_ref().or(DEFAULT_EMPTY_STRING)
5708                                                }
5709                                                _ => None,
5710                                            }
5711                                        },
5712                                        write: |settings_content, value| {
5713                                            match settings_content
5714                                                .terminal
5715                                                .get_or_insert_default()
5716                                                .project
5717                                                .shell
5718                                                .as_mut()
5719                                            {
5720                                                Some(settings::Shell::WithArguments { title_override, .. }) => {
5721                                                    *title_override = value.filter(|s| !s.is_empty())
5722                                                }
5723                                                _ => return,
5724                                            }
5725                                        },
5726                                    }),
5727                                    metadata: None,
5728                                },
5729                            ],
5730                        })
5731                        .collect(),
5732                }),
5733                SettingsPageItem::DynamicItem(DynamicItem {
5734                    discriminant: SettingItem {
5735                        files: USER | PROJECT,
5736                        title: "Working Directory",
5737                        description: "What working directory to use when launching the terminal.",
5738                        field: Box::new(SettingField {
5739                            json_path: Some("terminal.working_directory$"),
5740                            pick: |settings_content| {
5741                                Some(&dynamic_variants::<settings::WorkingDirectory>()[
5742                                    settings_content
5743                                        .terminal
5744                                        .as_ref()?
5745                                        .project
5746                                        .working_directory
5747                                        .as_ref()?
5748                                        .discriminant() as usize
5749                                ])
5750                            },
5751                            write: |settings_content, value| {
5752                                let Some(value) = value else {
5753                                    if let Some(terminal) = settings_content.terminal.as_mut() {
5754                                        terminal.project.working_directory = None;
5755                                    }
5756                                    return;
5757                                };
5758                                let settings_value = settings_content
5759                                    .terminal
5760                                    .get_or_insert_default()
5761                                    .project
5762                                    .working_directory
5763                                    .get_or_insert_with(|| settings::WorkingDirectory::CurrentProjectDirectory);
5764                                *settings_value = match value {
5765                                    settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => {
5766                                        settings::WorkingDirectory::CurrentProjectDirectory
5767                                    }
5768                                    settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => {
5769                                        settings::WorkingDirectory::FirstProjectDirectory
5770                                    }
5771                                    settings::WorkingDirectoryDiscriminants::AlwaysHome => {
5772                                        settings::WorkingDirectory::AlwaysHome
5773                                    }
5774                                    settings::WorkingDirectoryDiscriminants::Always => {
5775                                        let directory = match settings_value {
5776                                            settings::WorkingDirectory::Always { .. } => return,
5777                                            _ => String::new(),
5778                                        };
5779                                        settings::WorkingDirectory::Always { directory }
5780                                    }
5781                                };
5782                            },
5783                        }),
5784                        metadata: None,
5785                    },
5786                    pick_discriminant: |settings_content| {
5787                        Some(
5788                            settings_content
5789                                .terminal
5790                                .as_ref()?
5791                                .project
5792                                .working_directory
5793                                .as_ref()?
5794                                .discriminant() as usize,
5795                        )
5796                    },
5797                    fields: dynamic_variants::<settings::WorkingDirectory>()
5798                        .into_iter()
5799                        .map(|variant| match variant {
5800                            settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => vec![],
5801                            settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => vec![],
5802                            settings::WorkingDirectoryDiscriminants::AlwaysHome => vec![],
5803                            settings::WorkingDirectoryDiscriminants::Always => vec![SettingItem {
5804                                files: USER | PROJECT,
5805                                title: "Directory",
5806                                description: "The directory path to use (will be shell expanded).",
5807                                field: Box::new(SettingField {
5808                                    json_path: Some("terminal.working_directory.always"),
5809                                    pick: |settings_content| {
5810                                        match settings_content.terminal.as_ref()?.project.working_directory.as_ref() {
5811                                            Some(settings::WorkingDirectory::Always { directory }) => Some(directory),
5812                                            _ => None,
5813                                        }
5814                                    },
5815                                    write: |settings_content, value| {
5816                                        let value = value.unwrap_or_default();
5817                                        match settings_content
5818                                            .terminal
5819                                            .get_or_insert_default()
5820                                            .project
5821                                            .working_directory
5822                                            .as_mut()
5823                                        {
5824                                            Some(settings::WorkingDirectory::Always { directory }) => *directory = value,
5825                                            _ => return,
5826                                        }
5827                                    },
5828                                }),
5829                                metadata: None,
5830                            }],
5831                        })
5832                        .collect(),
5833                }),
5834                SettingsPageItem::SettingItem(SettingItem {
5835                    title: "Environment Variables",
5836                    description: "Key-value pairs to add to the terminal's environment.",
5837                    field: Box::new(
5838                        SettingField {
5839                            json_path: Some("terminal.env"),
5840                            pick: |settings_content| settings_content.terminal.as_ref()?.project.env.as_ref(),
5841                            write: |settings_content, value| {
5842                                settings_content.terminal.get_or_insert_default().project.env = value;
5843                            },
5844                        }
5845                        .unimplemented(),
5846                    ),
5847                    metadata: None,
5848                    files: USER | PROJECT,
5849                }),
5850                SettingsPageItem::SettingItem(SettingItem {
5851                    title: "Detect Virtual Environment",
5852                    description: "Activates the Python virtual environment, if one is found, in the terminal's working directory.",
5853                    field: Box::new(
5854                        SettingField {
5855                            json_path: Some("terminal.detect_venv"),
5856                            pick: |settings_content| settings_content.terminal.as_ref()?.project.detect_venv.as_ref(),
5857                            write: |settings_content, value| {
5858                                settings_content
5859                                    .terminal
5860                                    .get_or_insert_default()
5861                                    .project
5862                                    .detect_venv = value;
5863                            },
5864                        }
5865                        .unimplemented(),
5866                    ),
5867                    metadata: None,
5868                    files: USER | PROJECT,
5869                }),
5870            ]
5871    }
5872
5873    fn font_section() -> [SettingsPageItem; 6] {
5874        [
5875            SettingsPageItem::SectionHeader("Font"),
5876            SettingsPageItem::SettingItem(SettingItem {
5877                title: "Font Size",
5878                description: "Font size for terminal text. If not set, defaults to buffer font size.",
5879                field: Box::new(SettingField {
5880                    json_path: Some("terminal.font_size"),
5881                    pick: |settings_content| {
5882                        settings_content
5883                            .terminal
5884                            .as_ref()
5885                            .and_then(|terminal| terminal.font_size.as_ref())
5886                            .or(settings_content.theme.buffer_font_size.as_ref())
5887                    },
5888                    write: |settings_content, value| {
5889                        settings_content.terminal.get_or_insert_default().font_size = value;
5890                    },
5891                }),
5892                metadata: None,
5893                files: USER,
5894            }),
5895            SettingsPageItem::SettingItem(SettingItem {
5896                title: "Font Family",
5897                description: "Font family for terminal text. If not set, defaults to buffer font family.",
5898                field: Box::new(SettingField {
5899                    json_path: Some("terminal.font_family"),
5900                    pick: |settings_content| {
5901                        settings_content
5902                            .terminal
5903                            .as_ref()
5904                            .and_then(|terminal| terminal.font_family.as_ref())
5905                            .or(settings_content.theme.buffer_font_family.as_ref())
5906                    },
5907                    write: |settings_content, value| {
5908                        settings_content
5909                            .terminal
5910                            .get_or_insert_default()
5911                            .font_family = value;
5912                    },
5913                }),
5914                metadata: None,
5915                files: USER,
5916            }),
5917            SettingsPageItem::SettingItem(SettingItem {
5918                title: "Font Fallbacks",
5919                description: "Font fallbacks for terminal text. If not set, defaults to buffer font fallbacks.",
5920                field: Box::new(
5921                    SettingField {
5922                        json_path: Some("terminal.font_fallbacks"),
5923                        pick: |settings_content| {
5924                            settings_content
5925                                .terminal
5926                                .as_ref()
5927                                .and_then(|terminal| terminal.font_fallbacks.as_ref())
5928                                .or(settings_content.theme.buffer_font_fallbacks.as_ref())
5929                        },
5930                        write: |settings_content, value| {
5931                            settings_content
5932                                .terminal
5933                                .get_or_insert_default()
5934                                .font_fallbacks = value;
5935                        },
5936                    }
5937                    .unimplemented(),
5938                ),
5939                metadata: None,
5940                files: USER,
5941            }),
5942            SettingsPageItem::SettingItem(SettingItem {
5943                title: "Font Weight",
5944                description: "Font weight for terminal text in CSS weight units (100-900).",
5945                field: Box::new(SettingField {
5946                    json_path: Some("terminal.font_weight"),
5947                    pick: |settings_content| {
5948                        settings_content.terminal.as_ref()?.font_weight.as_ref()
5949                    },
5950                    write: |settings_content, value| {
5951                        settings_content
5952                            .terminal
5953                            .get_or_insert_default()
5954                            .font_weight = value;
5955                    },
5956                }),
5957                metadata: None,
5958                files: USER,
5959            }),
5960            SettingsPageItem::SettingItem(SettingItem {
5961                title: "Font Features",
5962                description: "Font features for terminal text.",
5963                field: Box::new(
5964                    SettingField {
5965                        json_path: Some("terminal.font_features"),
5966                        pick: |settings_content| {
5967                            settings_content
5968                                .terminal
5969                                .as_ref()
5970                                .and_then(|terminal| terminal.font_features.as_ref())
5971                                .or(settings_content.theme.buffer_font_features.as_ref())
5972                        },
5973                        write: |settings_content, value| {
5974                            settings_content
5975                                .terminal
5976                                .get_or_insert_default()
5977                                .font_features = value;
5978                        },
5979                    }
5980                    .unimplemented(),
5981                ),
5982                metadata: None,
5983                files: USER,
5984            }),
5985        ]
5986    }
5987
5988    fn display_settings_section() -> [SettingsPageItem; 6] {
5989        [
5990            SettingsPageItem::SectionHeader("Display Settings"),
5991            SettingsPageItem::SettingItem(SettingItem {
5992                title: "Line Height",
5993                description: "Line height for terminal text.",
5994                field: Box::new(
5995                    SettingField {
5996                        json_path: Some("terminal.line_height"),
5997                        pick: |settings_content| {
5998                            settings_content.terminal.as_ref()?.line_height.as_ref()
5999                        },
6000                        write: |settings_content, value| {
6001                            settings_content
6002                                .terminal
6003                                .get_or_insert_default()
6004                                .line_height = value;
6005                        },
6006                    }
6007                    .unimplemented(),
6008                ),
6009                metadata: None,
6010                files: USER,
6011            }),
6012            SettingsPageItem::SettingItem(SettingItem {
6013                title: "Cursor Shape",
6014                description: "Default cursor shape for the terminal (bar, block, underline, or hollow).",
6015                field: Box::new(SettingField {
6016                    json_path: Some("terminal.cursor_shape"),
6017                    pick: |settings_content| {
6018                        settings_content.terminal.as_ref()?.cursor_shape.as_ref()
6019                    },
6020                    write: |settings_content, value| {
6021                        settings_content
6022                            .terminal
6023                            .get_or_insert_default()
6024                            .cursor_shape = value;
6025                    },
6026                }),
6027                metadata: None,
6028                files: USER,
6029            }),
6030            SettingsPageItem::SettingItem(SettingItem {
6031                title: "Cursor Blinking",
6032                description: "Sets the cursor blinking behavior in the terminal.",
6033                field: Box::new(SettingField {
6034                    json_path: Some("terminal.blinking"),
6035                    pick: |settings_content| settings_content.terminal.as_ref()?.blinking.as_ref(),
6036                    write: |settings_content, value| {
6037                        settings_content.terminal.get_or_insert_default().blinking = value;
6038                    },
6039                }),
6040                metadata: None,
6041                files: USER,
6042            }),
6043            SettingsPageItem::SettingItem(SettingItem {
6044                title: "Alternate Scroll",
6045                description: "Whether alternate scroll mode is active by default (converts mouse scroll to arrow keys in apps like Vim).",
6046                field: Box::new(SettingField {
6047                    json_path: Some("terminal.alternate_scroll"),
6048                    pick: |settings_content| {
6049                        settings_content
6050                            .terminal
6051                            .as_ref()?
6052                            .alternate_scroll
6053                            .as_ref()
6054                    },
6055                    write: |settings_content, value| {
6056                        settings_content
6057                            .terminal
6058                            .get_or_insert_default()
6059                            .alternate_scroll = value;
6060                    },
6061                }),
6062                metadata: None,
6063                files: USER,
6064            }),
6065            SettingsPageItem::SettingItem(SettingItem {
6066                title: "Minimum Contrast",
6067                description: "The minimum APCA perceptual contrast between foreground and background colors (0-106).",
6068                field: Box::new(SettingField {
6069                    json_path: Some("terminal.minimum_contrast"),
6070                    pick: |settings_content| {
6071                        settings_content
6072                            .terminal
6073                            .as_ref()?
6074                            .minimum_contrast
6075                            .as_ref()
6076                    },
6077                    write: |settings_content, value| {
6078                        settings_content
6079                            .terminal
6080                            .get_or_insert_default()
6081                            .minimum_contrast = value;
6082                    },
6083                }),
6084                metadata: None,
6085                files: USER,
6086            }),
6087        ]
6088    }
6089
6090    fn behavior_settings_section() -> [SettingsPageItem; 4] {
6091        [
6092            SettingsPageItem::SectionHeader("Behavior Settings"),
6093            SettingsPageItem::SettingItem(SettingItem {
6094                title: "Option As Meta",
6095                description: "Whether the option key behaves as the meta key.",
6096                field: Box::new(SettingField {
6097                    json_path: Some("terminal.option_as_meta"),
6098                    pick: |settings_content| {
6099                        settings_content.terminal.as_ref()?.option_as_meta.as_ref()
6100                    },
6101                    write: |settings_content, value| {
6102                        settings_content
6103                            .terminal
6104                            .get_or_insert_default()
6105                            .option_as_meta = value;
6106                    },
6107                }),
6108                metadata: None,
6109                files: USER,
6110            }),
6111            SettingsPageItem::SettingItem(SettingItem {
6112                title: "Copy On Select",
6113                description: "Whether selecting text in the terminal automatically copies to the system clipboard.",
6114                field: Box::new(SettingField {
6115                    json_path: Some("terminal.copy_on_select"),
6116                    pick: |settings_content| {
6117                        settings_content.terminal.as_ref()?.copy_on_select.as_ref()
6118                    },
6119                    write: |settings_content, value| {
6120                        settings_content
6121                            .terminal
6122                            .get_or_insert_default()
6123                            .copy_on_select = value;
6124                    },
6125                }),
6126                metadata: None,
6127                files: USER,
6128            }),
6129            SettingsPageItem::SettingItem(SettingItem {
6130                title: "Keep Selection On Copy",
6131                description: "Whether to keep the text selection after copying it to the clipboard.",
6132                field: Box::new(SettingField {
6133                    json_path: Some("terminal.keep_selection_on_copy"),
6134                    pick: |settings_content| {
6135                        settings_content
6136                            .terminal
6137                            .as_ref()?
6138                            .keep_selection_on_copy
6139                            .as_ref()
6140                    },
6141                    write: |settings_content, value| {
6142                        settings_content
6143                            .terminal
6144                            .get_or_insert_default()
6145                            .keep_selection_on_copy = value;
6146                    },
6147                }),
6148                metadata: None,
6149                files: USER,
6150            }),
6151        ]
6152    }
6153
6154    fn layout_settings_section() -> [SettingsPageItem; 3] {
6155        [
6156            SettingsPageItem::SectionHeader("Layout Settings"),
6157            SettingsPageItem::SettingItem(SettingItem {
6158                title: "Default Width",
6159                description: "Default width when the terminal is docked to the left or right (in pixels).",
6160                field: Box::new(SettingField {
6161                    json_path: Some("terminal.default_width"),
6162                    pick: |settings_content| {
6163                        settings_content.terminal.as_ref()?.default_width.as_ref()
6164                    },
6165                    write: |settings_content, value| {
6166                        settings_content
6167                            .terminal
6168                            .get_or_insert_default()
6169                            .default_width = value;
6170                    },
6171                }),
6172                metadata: None,
6173                files: USER,
6174            }),
6175            SettingsPageItem::SettingItem(SettingItem {
6176                title: "Default Height",
6177                description: "Default height when the terminal is docked to the bottom (in pixels).",
6178                field: Box::new(SettingField {
6179                    json_path: Some("terminal.default_height"),
6180                    pick: |settings_content| {
6181                        settings_content.terminal.as_ref()?.default_height.as_ref()
6182                    },
6183                    write: |settings_content, value| {
6184                        settings_content
6185                            .terminal
6186                            .get_or_insert_default()
6187                            .default_height = value;
6188                    },
6189                }),
6190                metadata: None,
6191                files: USER,
6192            }),
6193        ]
6194    }
6195
6196    fn advanced_settings_section() -> [SettingsPageItem; 3] {
6197        [
6198            SettingsPageItem::SectionHeader("Advanced Settings"),
6199            SettingsPageItem::SettingItem(SettingItem {
6200                title: "Max Scroll History Lines",
6201                description: "Maximum number of lines to keep in scrollback history (max: 100,000; 0 disables scrolling).",
6202                field: Box::new(SettingField {
6203                    json_path: Some("terminal.max_scroll_history_lines"),
6204                    pick: |settings_content| {
6205                        settings_content
6206                            .terminal
6207                            .as_ref()?
6208                            .max_scroll_history_lines
6209                            .as_ref()
6210                    },
6211                    write: |settings_content, value| {
6212                        settings_content
6213                            .terminal
6214                            .get_or_insert_default()
6215                            .max_scroll_history_lines = value;
6216                    },
6217                }),
6218                metadata: None,
6219                files: USER,
6220            }),
6221            SettingsPageItem::SettingItem(SettingItem {
6222                title: "Scroll Multiplier",
6223                description: "The multiplier for scrolling in the terminal with the mouse wheel",
6224                field: Box::new(SettingField {
6225                    json_path: Some("terminal.scroll_multiplier"),
6226                    pick: |settings_content| {
6227                        settings_content
6228                            .terminal
6229                            .as_ref()?
6230                            .scroll_multiplier
6231                            .as_ref()
6232                    },
6233                    write: |settings_content, value| {
6234                        settings_content
6235                            .terminal
6236                            .get_or_insert_default()
6237                            .scroll_multiplier = value;
6238                    },
6239                }),
6240                metadata: None,
6241                files: USER,
6242            }),
6243        ]
6244    }
6245
6246    fn toolbar_section() -> [SettingsPageItem; 2] {
6247        [
6248            SettingsPageItem::SectionHeader("Toolbar"),
6249            SettingsPageItem::SettingItem(SettingItem {
6250                title: "Breadcrumbs",
6251                description: "Display the terminal title in breadcrumbs inside the terminal pane.",
6252                field: Box::new(SettingField {
6253                    json_path: Some("terminal.toolbar.breadcrumbs"),
6254                    pick: |settings_content| {
6255                        settings_content
6256                            .terminal
6257                            .as_ref()?
6258                            .toolbar
6259                            .as_ref()?
6260                            .breadcrumbs
6261                            .as_ref()
6262                    },
6263                    write: |settings_content, value| {
6264                        settings_content
6265                            .terminal
6266                            .get_or_insert_default()
6267                            .toolbar
6268                            .get_or_insert_default()
6269                            .breadcrumbs = value;
6270                    },
6271                }),
6272                metadata: None,
6273                files: USER,
6274            }),
6275        ]
6276    }
6277
6278    fn scrollbar_section() -> [SettingsPageItem; 2] {
6279        [
6280            SettingsPageItem::SectionHeader("Scrollbar"),
6281            SettingsPageItem::SettingItem(SettingItem {
6282                title: "Show Scrollbar",
6283                description: "When to show the scrollbar in the terminal.",
6284                field: Box::new(SettingField {
6285                    json_path: Some("terminal.scrollbar.show"),
6286                    pick: |settings_content| {
6287                        show_scrollbar_or_editor(settings_content, |settings_content| {
6288                            settings_content
6289                                .terminal
6290                                .as_ref()?
6291                                .scrollbar
6292                                .as_ref()?
6293                                .show
6294                                .as_ref()
6295                        })
6296                    },
6297                    write: |settings_content, value| {
6298                        settings_content
6299                            .terminal
6300                            .get_or_insert_default()
6301                            .scrollbar
6302                            .get_or_insert_default()
6303                            .show = value;
6304                    },
6305                }),
6306                metadata: None,
6307                files: USER,
6308            }),
6309        ]
6310    }
6311
6312    SettingsPage {
6313        title: "Terminal",
6314        items: concat_sections![
6315            environment_section(),
6316            font_section(),
6317            display_settings_section(),
6318            behavior_settings_section(),
6319            layout_settings_section(),
6320            advanced_settings_section(),
6321            toolbar_section(),
6322            scrollbar_section(),
6323        ],
6324    }
6325}
6326
6327fn version_control_page() -> SettingsPage {
6328    fn git_integration_section() -> [SettingsPageItem; 2] {
6329        [
6330            SettingsPageItem::SectionHeader("Git Integration"),
6331            SettingsPageItem::DynamicItem(DynamicItem {
6332                discriminant: SettingItem {
6333                    files: USER,
6334                    title: "Disable Git Integration",
6335                    description: "Disable all Git integration features in Zed.",
6336                    field: Box::new(SettingField::<bool> {
6337                        json_path: Some("git.disable_git"),
6338                        pick: |settings_content| {
6339                            settings_content
6340                                .git
6341                                .as_ref()?
6342                                .enabled
6343                                .as_ref()?
6344                                .disable_git
6345                                .as_ref()
6346                        },
6347                        write: |settings_content, value| {
6348                            settings_content
6349                                .git
6350                                .get_or_insert_default()
6351                                .enabled
6352                                .get_or_insert_default()
6353                                .disable_git = value;
6354                        },
6355                    }),
6356                    metadata: None,
6357                },
6358                pick_discriminant: |settings_content| {
6359                    let disabled = settings_content
6360                        .git
6361                        .as_ref()?
6362                        .enabled
6363                        .as_ref()?
6364                        .disable_git
6365                        .unwrap_or(false);
6366                    Some(if disabled { 0 } else { 1 })
6367                },
6368                fields: vec![
6369                    vec![],
6370                    vec![
6371                        SettingItem {
6372                            files: USER,
6373                            title: "Enable Git Status",
6374                            description: "Show Git status information in the editor.",
6375                            field: Box::new(SettingField::<bool> {
6376                                json_path: Some("git.enable_status"),
6377                                pick: |settings_content| {
6378                                    settings_content
6379                                        .git
6380                                        .as_ref()?
6381                                        .enabled
6382                                        .as_ref()?
6383                                        .enable_status
6384                                        .as_ref()
6385                                },
6386                                write: |settings_content, value| {
6387                                    settings_content
6388                                        .git
6389                                        .get_or_insert_default()
6390                                        .enabled
6391                                        .get_or_insert_default()
6392                                        .enable_status = value;
6393                                },
6394                            }),
6395                            metadata: None,
6396                        },
6397                        SettingItem {
6398                            files: USER,
6399                            title: "Enable Git Diff",
6400                            description: "Show Git diff information in the editor.",
6401                            field: Box::new(SettingField::<bool> {
6402                                json_path: Some("git.enable_diff"),
6403                                pick: |settings_content| {
6404                                    settings_content
6405                                        .git
6406                                        .as_ref()?
6407                                        .enabled
6408                                        .as_ref()?
6409                                        .enable_diff
6410                                        .as_ref()
6411                                },
6412                                write: |settings_content, value| {
6413                                    settings_content
6414                                        .git
6415                                        .get_or_insert_default()
6416                                        .enabled
6417                                        .get_or_insert_default()
6418                                        .enable_diff = value;
6419                                },
6420                            }),
6421                            metadata: None,
6422                        },
6423                    ],
6424                ],
6425            }),
6426        ]
6427    }
6428
6429    fn git_gutter_section() -> [SettingsPageItem; 3] {
6430        [
6431            SettingsPageItem::SectionHeader("Git Gutter"),
6432            SettingsPageItem::SettingItem(SettingItem {
6433                title: "Visibility",
6434                description: "Control whether Git status is shown in the editor's gutter.",
6435                field: Box::new(SettingField {
6436                    json_path: Some("git.git_gutter"),
6437                    pick: |settings_content| settings_content.git.as_ref()?.git_gutter.as_ref(),
6438                    write: |settings_content, value| {
6439                        settings_content.git.get_or_insert_default().git_gutter = value;
6440                    },
6441                }),
6442                metadata: None,
6443                files: USER,
6444            }),
6445            // todo(settings_ui): Figure out the right default for this value in default.json
6446            SettingsPageItem::SettingItem(SettingItem {
6447                title: "Debounce",
6448                description: "Debounce threshold in milliseconds after which changes are reflected in the Git gutter.",
6449                field: Box::new(SettingField {
6450                    json_path: Some("git.gutter_debounce"),
6451                    pick: |settings_content| {
6452                        settings_content.git.as_ref()?.gutter_debounce.as_ref()
6453                    },
6454                    write: |settings_content, value| {
6455                        settings_content.git.get_or_insert_default().gutter_debounce = value;
6456                    },
6457                }),
6458                metadata: None,
6459                files: USER,
6460            }),
6461        ]
6462    }
6463
6464    fn inline_git_blame_section() -> [SettingsPageItem; 6] {
6465        [
6466            SettingsPageItem::SectionHeader("Inline Git Blame"),
6467            SettingsPageItem::SettingItem(SettingItem {
6468                title: "Enabled",
6469                description: "Whether or not to show Git blame data inline in the currently focused line.",
6470                field: Box::new(SettingField {
6471                    json_path: Some("git.inline_blame.enabled"),
6472                    pick: |settings_content| {
6473                        settings_content
6474                            .git
6475                            .as_ref()?
6476                            .inline_blame
6477                            .as_ref()?
6478                            .enabled
6479                            .as_ref()
6480                    },
6481                    write: |settings_content, value| {
6482                        settings_content
6483                            .git
6484                            .get_or_insert_default()
6485                            .inline_blame
6486                            .get_or_insert_default()
6487                            .enabled = value;
6488                    },
6489                }),
6490                metadata: None,
6491                files: USER,
6492            }),
6493            SettingsPageItem::SettingItem(SettingItem {
6494                title: "Delay",
6495                description: "The delay after which the inline blame information is shown.",
6496                field: Box::new(SettingField {
6497                    json_path: Some("git.inline_blame.delay_ms"),
6498                    pick: |settings_content| {
6499                        settings_content
6500                            .git
6501                            .as_ref()?
6502                            .inline_blame
6503                            .as_ref()?
6504                            .delay_ms
6505                            .as_ref()
6506                    },
6507                    write: |settings_content, value| {
6508                        settings_content
6509                            .git
6510                            .get_or_insert_default()
6511                            .inline_blame
6512                            .get_or_insert_default()
6513                            .delay_ms = value;
6514                    },
6515                }),
6516                metadata: None,
6517                files: USER,
6518            }),
6519            SettingsPageItem::SettingItem(SettingItem {
6520                title: "Padding",
6521                description: "Padding between the end of the source line and the start of the inline blame in columns.",
6522                field: Box::new(SettingField {
6523                    json_path: Some("git.inline_blame.padding"),
6524                    pick: |settings_content| {
6525                        settings_content
6526                            .git
6527                            .as_ref()?
6528                            .inline_blame
6529                            .as_ref()?
6530                            .padding
6531                            .as_ref()
6532                    },
6533                    write: |settings_content, value| {
6534                        settings_content
6535                            .git
6536                            .get_or_insert_default()
6537                            .inline_blame
6538                            .get_or_insert_default()
6539                            .padding = value;
6540                    },
6541                }),
6542                metadata: None,
6543                files: USER,
6544            }),
6545            SettingsPageItem::SettingItem(SettingItem {
6546                title: "Minimum Column",
6547                description: "The minimum column number at which to show the inline blame information.",
6548                field: Box::new(SettingField {
6549                    json_path: Some("git.inline_blame.min_column"),
6550                    pick: |settings_content| {
6551                        settings_content
6552                            .git
6553                            .as_ref()?
6554                            .inline_blame
6555                            .as_ref()?
6556                            .min_column
6557                            .as_ref()
6558                    },
6559                    write: |settings_content, value| {
6560                        settings_content
6561                            .git
6562                            .get_or_insert_default()
6563                            .inline_blame
6564                            .get_or_insert_default()
6565                            .min_column = value;
6566                    },
6567                }),
6568                metadata: None,
6569                files: USER,
6570            }),
6571            SettingsPageItem::SettingItem(SettingItem {
6572                title: "Show Commit Summary",
6573                description: "Show commit summary as part of the inline blame.",
6574                field: Box::new(SettingField {
6575                    json_path: Some("git.inline_blame.show_commit_summary"),
6576                    pick: |settings_content| {
6577                        settings_content
6578                            .git
6579                            .as_ref()?
6580                            .inline_blame
6581                            .as_ref()?
6582                            .show_commit_summary
6583                            .as_ref()
6584                    },
6585                    write: |settings_content, value| {
6586                        settings_content
6587                            .git
6588                            .get_or_insert_default()
6589                            .inline_blame
6590                            .get_or_insert_default()
6591                            .show_commit_summary = value;
6592                    },
6593                }),
6594                metadata: None,
6595                files: USER,
6596            }),
6597        ]
6598    }
6599
6600    fn git_blame_view_section() -> [SettingsPageItem; 2] {
6601        [
6602            SettingsPageItem::SectionHeader("Git Blame View"),
6603            SettingsPageItem::SettingItem(SettingItem {
6604                title: "Show Avatar",
6605                description: "Show the avatar of the author of the commit.",
6606                field: Box::new(SettingField {
6607                    json_path: Some("git.blame.show_avatar"),
6608                    pick: |settings_content| {
6609                        settings_content
6610                            .git
6611                            .as_ref()?
6612                            .blame
6613                            .as_ref()?
6614                            .show_avatar
6615                            .as_ref()
6616                    },
6617                    write: |settings_content, value| {
6618                        settings_content
6619                            .git
6620                            .get_or_insert_default()
6621                            .blame
6622                            .get_or_insert_default()
6623                            .show_avatar = value;
6624                    },
6625                }),
6626                metadata: None,
6627                files: USER,
6628            }),
6629        ]
6630    }
6631
6632    fn branch_picker_section() -> [SettingsPageItem; 2] {
6633        [
6634            SettingsPageItem::SectionHeader("Branch Picker"),
6635            SettingsPageItem::SettingItem(SettingItem {
6636                title: "Show Author Name",
6637                description: "Show author name as part of the commit information in branch picker.",
6638                field: Box::new(SettingField {
6639                    json_path: Some("git.branch_picker.show_author_name"),
6640                    pick: |settings_content| {
6641                        settings_content
6642                            .git
6643                            .as_ref()?
6644                            .branch_picker
6645                            .as_ref()?
6646                            .show_author_name
6647                            .as_ref()
6648                    },
6649                    write: |settings_content, value| {
6650                        settings_content
6651                            .git
6652                            .get_or_insert_default()
6653                            .branch_picker
6654                            .get_or_insert_default()
6655                            .show_author_name = value;
6656                    },
6657                }),
6658                metadata: None,
6659                files: USER,
6660            }),
6661        ]
6662    }
6663
6664    fn git_hunks_section() -> [SettingsPageItem; 3] {
6665        [
6666            SettingsPageItem::SectionHeader("Git Hunks"),
6667            SettingsPageItem::SettingItem(SettingItem {
6668                title: "Hunk Style",
6669                description: "How Git hunks are displayed visually in the editor.",
6670                field: Box::new(SettingField {
6671                    json_path: Some("git.hunk_style"),
6672                    pick: |settings_content| settings_content.git.as_ref()?.hunk_style.as_ref(),
6673                    write: |settings_content, value| {
6674                        settings_content.git.get_or_insert_default().hunk_style = value;
6675                    },
6676                }),
6677                metadata: None,
6678                files: USER,
6679            }),
6680            SettingsPageItem::SettingItem(SettingItem {
6681                title: "Path Style",
6682                description: "Should the name or path be displayed first in the git view.",
6683                field: Box::new(SettingField {
6684                    json_path: Some("git.path_style"),
6685                    pick: |settings_content| settings_content.git.as_ref()?.path_style.as_ref(),
6686                    write: |settings_content, value| {
6687                        settings_content.git.get_or_insert_default().path_style = value;
6688                    },
6689                }),
6690                metadata: None,
6691                files: USER,
6692            }),
6693        ]
6694    }
6695
6696    SettingsPage {
6697        title: "Version Control",
6698        items: concat_sections![
6699            git_integration_section(),
6700            git_gutter_section(),
6701            inline_git_blame_section(),
6702            git_blame_view_section(),
6703            branch_picker_section(),
6704            git_hunks_section(),
6705        ],
6706    }
6707}
6708
6709fn collaboration_page() -> SettingsPage {
6710    fn calls_section() -> [SettingsPageItem; 3] {
6711        [
6712            SettingsPageItem::SectionHeader("Calls"),
6713            SettingsPageItem::SettingItem(SettingItem {
6714                title: "Mute On Join",
6715                description: "Whether the microphone should be muted when joining a channel or a call.",
6716                field: Box::new(SettingField {
6717                    json_path: Some("calls.mute_on_join"),
6718                    pick: |settings_content| settings_content.calls.as_ref()?.mute_on_join.as_ref(),
6719                    write: |settings_content, value| {
6720                        settings_content.calls.get_or_insert_default().mute_on_join = value;
6721                    },
6722                }),
6723                metadata: None,
6724                files: USER,
6725            }),
6726            SettingsPageItem::SettingItem(SettingItem {
6727                title: "Share On Join",
6728                description: "Whether your current project should be shared when joining an empty channel.",
6729                field: Box::new(SettingField {
6730                    json_path: Some("calls.share_on_join"),
6731                    pick: |settings_content| {
6732                        settings_content.calls.as_ref()?.share_on_join.as_ref()
6733                    },
6734                    write: |settings_content, value| {
6735                        settings_content.calls.get_or_insert_default().share_on_join = value;
6736                    },
6737                }),
6738                metadata: None,
6739                files: USER,
6740            }),
6741        ]
6742    }
6743
6744    fn experimental_section() -> [SettingsPageItem; 6] {
6745        [
6746            SettingsPageItem::SectionHeader("Experimental"),
6747            SettingsPageItem::SettingItem(SettingItem {
6748                title: "Rodio Audio",
6749                description: "Opt into the new audio system.",
6750                field: Box::new(SettingField {
6751                    json_path: Some("audio.experimental.rodio_audio"),
6752                    pick: |settings_content| settings_content.audio.as_ref()?.rodio_audio.as_ref(),
6753                    write: |settings_content, value| {
6754                        settings_content.audio.get_or_insert_default().rodio_audio = value;
6755                    },
6756                }),
6757                metadata: None,
6758                files: USER,
6759            }),
6760            SettingsPageItem::SettingItem(SettingItem {
6761                title: "Auto Microphone Volume",
6762                description: "Automatically adjust microphone volume (requires rodio audio).",
6763                field: Box::new(SettingField {
6764                    json_path: Some("audio.experimental.auto_microphone_volume"),
6765                    pick: |settings_content| {
6766                        settings_content
6767                            .audio
6768                            .as_ref()?
6769                            .auto_microphone_volume
6770                            .as_ref()
6771                    },
6772                    write: |settings_content, value| {
6773                        settings_content
6774                            .audio
6775                            .get_or_insert_default()
6776                            .auto_microphone_volume = value;
6777                    },
6778                }),
6779                metadata: None,
6780                files: USER,
6781            }),
6782            SettingsPageItem::SettingItem(SettingItem {
6783                title: "Auto Speaker Volume",
6784                description: "Automatically adjust volume of other call members (requires rodio audio).",
6785                field: Box::new(SettingField {
6786                    json_path: Some("audio.experimental.auto_speaker_volume"),
6787                    pick: |settings_content| {
6788                        settings_content
6789                            .audio
6790                            .as_ref()?
6791                            .auto_speaker_volume
6792                            .as_ref()
6793                    },
6794                    write: |settings_content, value| {
6795                        settings_content
6796                            .audio
6797                            .get_or_insert_default()
6798                            .auto_speaker_volume = value;
6799                    },
6800                }),
6801                metadata: None,
6802                files: USER,
6803            }),
6804            SettingsPageItem::SettingItem(SettingItem {
6805                title: "Denoise",
6806                description: "Remove background noises (requires rodio audio).",
6807                field: Box::new(SettingField {
6808                    json_path: Some("audio.experimental.denoise"),
6809                    pick: |settings_content| settings_content.audio.as_ref()?.denoise.as_ref(),
6810                    write: |settings_content, value| {
6811                        settings_content.audio.get_or_insert_default().denoise = value;
6812                    },
6813                }),
6814                metadata: None,
6815                files: USER,
6816            }),
6817            SettingsPageItem::SettingItem(SettingItem {
6818                title: "Legacy Audio Compatible",
6819                description: "Use audio parameters compatible with previous versions (requires rodio audio).",
6820                field: Box::new(SettingField {
6821                    json_path: Some("audio.experimental.legacy_audio_compatible"),
6822                    pick: |settings_content| {
6823                        settings_content
6824                            .audio
6825                            .as_ref()?
6826                            .legacy_audio_compatible
6827                            .as_ref()
6828                    },
6829                    write: |settings_content, value| {
6830                        settings_content
6831                            .audio
6832                            .get_or_insert_default()
6833                            .legacy_audio_compatible = value;
6834                    },
6835                }),
6836                metadata: None,
6837                files: USER,
6838            }),
6839        ]
6840    }
6841
6842    SettingsPage {
6843        title: "Collaboration",
6844        items: concat_sections![calls_section(), experimental_section()],
6845    }
6846}
6847
6848fn ai_page() -> SettingsPage {
6849    fn general_section() -> [SettingsPageItem; 2] {
6850        [
6851            SettingsPageItem::SectionHeader("General"),
6852            SettingsPageItem::SettingItem(SettingItem {
6853                title: "Disable AI",
6854                description: "Whether to disable all AI features in Zed.",
6855                field: Box::new(SettingField {
6856                    json_path: Some("disable_ai"),
6857                    pick: |settings_content| settings_content.disable_ai.as_ref(),
6858                    write: |settings_content, value| {
6859                        settings_content.disable_ai = value;
6860                    },
6861                }),
6862                metadata: None,
6863                files: USER,
6864            }),
6865        ]
6866    }
6867
6868    fn agent_configuration_section() -> [SettingsPageItem; 12] {
6869        [
6870            SettingsPageItem::SectionHeader("Agent Configuration"),
6871            SettingsPageItem::SettingItem(SettingItem {
6872                title: "Always Allow Tool Actions",
6873                description: "When enabled, the agent can run potentially destructive actions without asking for your confirmation. This setting has no effect on external agents.",
6874                field: Box::new(SettingField {
6875                    json_path: Some("agent.always_allow_tool_actions"),
6876                    pick: |settings_content| {
6877                        settings_content
6878                            .agent
6879                            .as_ref()?
6880                            .always_allow_tool_actions
6881                            .as_ref()
6882                    },
6883                    write: |settings_content, value| {
6884                        settings_content
6885                            .agent
6886                            .get_or_insert_default()
6887                            .always_allow_tool_actions = value;
6888                    },
6889                }),
6890                metadata: None,
6891                files: USER,
6892            }),
6893            SettingsPageItem::SettingItem(SettingItem {
6894                title: "Single File Review",
6895                description: "When enabled, agent edits will also be displayed in single-file buffers for review.",
6896                field: Box::new(SettingField {
6897                    json_path: Some("agent.single_file_review"),
6898                    pick: |settings_content| {
6899                        settings_content.agent.as_ref()?.single_file_review.as_ref()
6900                    },
6901                    write: |settings_content, value| {
6902                        settings_content
6903                            .agent
6904                            .get_or_insert_default()
6905                            .single_file_review = value;
6906                    },
6907                }),
6908                metadata: None,
6909                files: USER,
6910            }),
6911            SettingsPageItem::SettingItem(SettingItem {
6912                title: "Enable Feedback",
6913                description: "Show voting thumbs up/down icon buttons for feedback on agent edits.",
6914                field: Box::new(SettingField {
6915                    json_path: Some("agent.enable_feedback"),
6916                    pick: |settings_content| {
6917                        settings_content.agent.as_ref()?.enable_feedback.as_ref()
6918                    },
6919                    write: |settings_content, value| {
6920                        settings_content
6921                            .agent
6922                            .get_or_insert_default()
6923                            .enable_feedback = value;
6924                    },
6925                }),
6926                metadata: None,
6927                files: USER,
6928            }),
6929            SettingsPageItem::SettingItem(SettingItem {
6930                title: "Notify When Agent Waiting",
6931                description: "Where to show notifications when the agent has completed its response or needs confirmation before running a tool action.",
6932                field: Box::new(SettingField {
6933                    json_path: Some("agent.notify_when_agent_waiting"),
6934                    pick: |settings_content| {
6935                        settings_content
6936                            .agent
6937                            .as_ref()?
6938                            .notify_when_agent_waiting
6939                            .as_ref()
6940                    },
6941                    write: |settings_content, value| {
6942                        settings_content
6943                            .agent
6944                            .get_or_insert_default()
6945                            .notify_when_agent_waiting = value;
6946                    },
6947                }),
6948                metadata: None,
6949                files: USER,
6950            }),
6951            SettingsPageItem::SettingItem(SettingItem {
6952                title: "Play Sound When Agent Done",
6953                description: "Whether to play a sound when the agent has either completed its response, or needs user input.",
6954                field: Box::new(SettingField {
6955                    json_path: Some("agent.play_sound_when_agent_done"),
6956                    pick: |settings_content| {
6957                        settings_content
6958                            .agent
6959                            .as_ref()?
6960                            .play_sound_when_agent_done
6961                            .as_ref()
6962                    },
6963                    write: |settings_content, value| {
6964                        settings_content
6965                            .agent
6966                            .get_or_insert_default()
6967                            .play_sound_when_agent_done = value;
6968                    },
6969                }),
6970                metadata: None,
6971                files: USER,
6972            }),
6973            SettingsPageItem::SettingItem(SettingItem {
6974                title: "Expand Edit Card",
6975                description: "Whether to have edit cards in the agent panel expanded, showing a Preview of the diff.",
6976                field: Box::new(SettingField {
6977                    json_path: Some("agent.expand_edit_card"),
6978                    pick: |settings_content| {
6979                        settings_content.agent.as_ref()?.expand_edit_card.as_ref()
6980                    },
6981                    write: |settings_content, value| {
6982                        settings_content
6983                            .agent
6984                            .get_or_insert_default()
6985                            .expand_edit_card = value;
6986                    },
6987                }),
6988                metadata: None,
6989                files: USER,
6990            }),
6991            SettingsPageItem::SettingItem(SettingItem {
6992                title: "Expand Terminal Card",
6993                description: "Whether to have terminal cards in the agent panel expanded, showing the whole command output.",
6994                field: Box::new(SettingField {
6995                    json_path: Some("agent.expand_terminal_card"),
6996                    pick: |settings_content| {
6997                        settings_content
6998                            .agent
6999                            .as_ref()?
7000                            .expand_terminal_card
7001                            .as_ref()
7002                    },
7003                    write: |settings_content, value| {
7004                        settings_content
7005                            .agent
7006                            .get_or_insert_default()
7007                            .expand_terminal_card = value;
7008                    },
7009                }),
7010                metadata: None,
7011                files: USER,
7012            }),
7013            SettingsPageItem::SettingItem(SettingItem {
7014                title: "Cancel Generation On Terminal Stop",
7015                description: "Whether clicking the stop button on a running terminal tool should also cancel the agent's generation. Note that this only applies to the stop button, not to ctrl+c inside the terminal.",
7016                field: Box::new(SettingField {
7017                    json_path: Some("agent.cancel_generation_on_terminal_stop"),
7018                    pick: |settings_content| {
7019                        settings_content
7020                            .agent
7021                            .as_ref()?
7022                            .cancel_generation_on_terminal_stop
7023                            .as_ref()
7024                    },
7025                    write: |settings_content, value| {
7026                        settings_content
7027                            .agent
7028                            .get_or_insert_default()
7029                            .cancel_generation_on_terminal_stop = value;
7030                    },
7031                }),
7032                metadata: None,
7033                files: USER,
7034            }),
7035            SettingsPageItem::SettingItem(SettingItem {
7036                title: "Use Modifier To Send",
7037                description: "Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages.",
7038                field: Box::new(SettingField {
7039                    json_path: Some("agent.use_modifier_to_send"),
7040                    pick: |settings_content| {
7041                        settings_content
7042                            .agent
7043                            .as_ref()?
7044                            .use_modifier_to_send
7045                            .as_ref()
7046                    },
7047                    write: |settings_content, value| {
7048                        settings_content
7049                            .agent
7050                            .get_or_insert_default()
7051                            .use_modifier_to_send = value;
7052                    },
7053                }),
7054                metadata: None,
7055                files: USER,
7056            }),
7057            SettingsPageItem::SettingItem(SettingItem {
7058                title: "Message Editor Min Lines",
7059                description: "Minimum number of lines to display in the agent message editor.",
7060                field: Box::new(SettingField {
7061                    json_path: Some("agent.message_editor_min_lines"),
7062                    pick: |settings_content| {
7063                        settings_content
7064                            .agent
7065                            .as_ref()?
7066                            .message_editor_min_lines
7067                            .as_ref()
7068                    },
7069                    write: |settings_content, value| {
7070                        settings_content
7071                            .agent
7072                            .get_or_insert_default()
7073                            .message_editor_min_lines = value;
7074                    },
7075                }),
7076                metadata: None,
7077                files: USER,
7078            }),
7079            SettingsPageItem::SettingItem(SettingItem {
7080                title: "Show Turn Stats",
7081                description: "Whether to show turn statistics like elapsed time during generation and final turn duration.",
7082                field: Box::new(SettingField {
7083                    json_path: Some("agent.show_turn_stats"),
7084                    pick: |settings_content| {
7085                        settings_content.agent.as_ref()?.show_turn_stats.as_ref()
7086                    },
7087                    write: |settings_content, value| {
7088                        settings_content
7089                            .agent
7090                            .get_or_insert_default()
7091                            .show_turn_stats = value;
7092                    },
7093                }),
7094                metadata: None,
7095                files: USER,
7096            }),
7097        ]
7098    }
7099
7100    fn context_servers_section() -> [SettingsPageItem; 2] {
7101        [
7102            SettingsPageItem::SectionHeader("Context Servers"),
7103            SettingsPageItem::SettingItem(SettingItem {
7104                title: "Context Server Timeout",
7105                description: "Default timeout in seconds for context server tool calls. Can be overridden per-server in context_servers configuration.",
7106                field: Box::new(SettingField {
7107                    json_path: Some("context_server_timeout"),
7108                    pick: |settings_content| {
7109                        settings_content.project.context_server_timeout.as_ref()
7110                    },
7111                    write: |settings_content, value| {
7112                        settings_content.project.context_server_timeout = value;
7113                    },
7114                }),
7115                metadata: None,
7116                files: USER | PROJECT,
7117            }),
7118        ]
7119    }
7120
7121    fn edit_prediction_display_sub_section() -> [SettingsPageItem; 2] {
7122        [
7123            SettingsPageItem::SettingItem(SettingItem {
7124                title: "Display Mode",
7125                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.",
7126                field: Box::new(SettingField {
7127                    json_path: Some("edit_prediction.display_mode"),
7128                    pick: |settings_content| {
7129                        settings_content
7130                            .project
7131                            .all_languages
7132                            .edit_predictions
7133                            .as_ref()?
7134                            .mode
7135                            .as_ref()
7136                    },
7137                    write: |settings_content, value| {
7138                        settings_content
7139                            .project
7140                            .all_languages
7141                            .edit_predictions
7142                            .get_or_insert_default()
7143                            .mode = value;
7144                    },
7145                }),
7146                metadata: None,
7147                files: USER,
7148            }),
7149            SettingsPageItem::SettingItem(SettingItem {
7150                title: "Display In Text Threads",
7151                description: "Whether edit predictions are enabled when editing text threads in the agent panel.",
7152                field: Box::new(SettingField {
7153                    json_path: Some("edit_prediction.in_text_threads"),
7154                    pick: |settings_content| {
7155                        settings_content
7156                            .project
7157                            .all_languages
7158                            .edit_predictions
7159                            .as_ref()?
7160                            .enabled_in_text_threads
7161                            .as_ref()
7162                    },
7163                    write: |settings_content, value| {
7164                        settings_content
7165                            .project
7166                            .all_languages
7167                            .edit_predictions
7168                            .get_or_insert_default()
7169                            .enabled_in_text_threads = value;
7170                    },
7171                }),
7172                metadata: None,
7173                files: USER,
7174            }),
7175        ]
7176    }
7177
7178    SettingsPage {
7179        title: "AI",
7180        items: concat_sections![
7181            general_section(),
7182            agent_configuration_section(),
7183            context_servers_section(),
7184            edit_prediction_language_settings_section(),
7185            edit_prediction_display_sub_section()
7186        ],
7187    }
7188}
7189
7190fn network_page() -> SettingsPage {
7191    fn network_section() -> [SettingsPageItem; 3] {
7192        [
7193            SettingsPageItem::SectionHeader("Network"),
7194            SettingsPageItem::SettingItem(SettingItem {
7195                title: "Proxy",
7196                description: "The proxy to use for network requests.",
7197                field: Box::new(SettingField {
7198                    json_path: Some("proxy"),
7199                    pick: |settings_content| settings_content.proxy.as_ref(),
7200                    write: |settings_content, value| {
7201                        settings_content.proxy = value;
7202                    },
7203                }),
7204                metadata: Some(Box::new(SettingsFieldMetadata {
7205                    placeholder: Some("socks5h://localhost:10808"),
7206                    ..Default::default()
7207                })),
7208                files: USER,
7209            }),
7210            SettingsPageItem::SettingItem(SettingItem {
7211                title: "Server URL",
7212                description: "The URL of the Zed server to connect to.",
7213                field: Box::new(SettingField {
7214                    json_path: Some("server_url"),
7215                    pick: |settings_content| settings_content.server_url.as_ref(),
7216                    write: |settings_content, value| {
7217                        settings_content.server_url = value;
7218                    },
7219                }),
7220                metadata: Some(Box::new(SettingsFieldMetadata {
7221                    placeholder: Some("https://zed.dev"),
7222                    ..Default::default()
7223                })),
7224                files: USER,
7225            }),
7226        ]
7227    }
7228
7229    SettingsPage {
7230        title: "Network",
7231        items: concat_sections![network_section()],
7232    }
7233}
7234
7235fn language_settings_field<T>(
7236    settings_content: &SettingsContent,
7237    get_language_setting_field: fn(&LanguageSettingsContent) -> Option<&T>,
7238) -> Option<&T> {
7239    let all_languages = &settings_content.project.all_languages;
7240
7241    active_language()
7242        .and_then(|current_language_name| {
7243            all_languages
7244                .languages
7245                .0
7246                .get(current_language_name.as_ref())
7247        })
7248        .and_then(get_language_setting_field)
7249        .or_else(|| get_language_setting_field(&all_languages.defaults))
7250}
7251
7252fn language_settings_field_mut<T>(
7253    settings_content: &mut SettingsContent,
7254    value: Option<T>,
7255    write: fn(&mut LanguageSettingsContent, Option<T>),
7256) {
7257    let all_languages = &mut settings_content.project.all_languages;
7258    let language_content = if let Some(current_language) = active_language() {
7259        all_languages
7260            .languages
7261            .0
7262            .entry(current_language.to_string())
7263            .or_default()
7264    } else {
7265        &mut all_languages.defaults
7266    };
7267    write(language_content, value);
7268}
7269
7270fn language_settings_data() -> Box<[SettingsPageItem]> {
7271    fn indentation_section() -> [SettingsPageItem; 5] {
7272        [
7273            SettingsPageItem::SectionHeader("Indentation"),
7274            SettingsPageItem::SettingItem(SettingItem {
7275                title: "Tab Size",
7276                description: "How many columns a tab should occupy.",
7277                field: Box::new(SettingField {
7278                    json_path: Some("languages.$(language).tab_size"), // TODO(cameron): not JQ syntax because not URL-safe
7279                    pick: |settings_content| {
7280                        language_settings_field(settings_content, |language| {
7281                            language.tab_size.as_ref()
7282                        })
7283                    },
7284                    write: |settings_content, value| {
7285                        language_settings_field_mut(settings_content, value, |language, value| {
7286                            language.tab_size = value;
7287                        })
7288                    },
7289                }),
7290                metadata: None,
7291                files: USER | PROJECT,
7292            }),
7293            SettingsPageItem::SettingItem(SettingItem {
7294                title: "Hard Tabs",
7295                description: "Whether to indent lines using tab characters, as opposed to multiple spaces.",
7296                field: Box::new(SettingField {
7297                    json_path: Some("languages.$(language).hard_tabs"),
7298                    pick: |settings_content| {
7299                        language_settings_field(settings_content, |language| {
7300                            language.hard_tabs.as_ref()
7301                        })
7302                    },
7303                    write: |settings_content, value| {
7304                        language_settings_field_mut(settings_content, value, |language, value| {
7305                            language.hard_tabs = value;
7306                        })
7307                    },
7308                }),
7309                metadata: None,
7310                files: USER | PROJECT,
7311            }),
7312            SettingsPageItem::SettingItem(SettingItem {
7313                title: "Auto Indent",
7314                description: "Whether indentation should be adjusted based on the context whilst typing.",
7315                field: Box::new(SettingField {
7316                    json_path: Some("languages.$(language).auto_indent"),
7317                    pick: |settings_content| {
7318                        language_settings_field(settings_content, |language| {
7319                            language.auto_indent.as_ref()
7320                        })
7321                    },
7322                    write: |settings_content, value| {
7323                        language_settings_field_mut(settings_content, value, |language, value| {
7324                            language.auto_indent = value;
7325                        })
7326                    },
7327                }),
7328                metadata: None,
7329                files: USER | PROJECT,
7330            }),
7331            SettingsPageItem::SettingItem(SettingItem {
7332                title: "Auto Indent On Paste",
7333                description: "Whether indentation of pasted content should be adjusted based on the context.",
7334                field: Box::new(SettingField {
7335                    json_path: Some("languages.$(language).auto_indent_on_paste"),
7336                    pick: |settings_content| {
7337                        language_settings_field(settings_content, |language| {
7338                            language.auto_indent_on_paste.as_ref()
7339                        })
7340                    },
7341                    write: |settings_content, value| {
7342                        language_settings_field_mut(settings_content, value, |language, value| {
7343                            language.auto_indent_on_paste = value;
7344                        })
7345                    },
7346                }),
7347                metadata: None,
7348                files: USER | PROJECT,
7349            }),
7350        ]
7351    }
7352
7353    fn wrapping_section() -> [SettingsPageItem; 6] {
7354        [
7355            SettingsPageItem::SectionHeader("Wrapping"),
7356            SettingsPageItem::SettingItem(SettingItem {
7357                title: "Soft Wrap",
7358                description: "How to soft-wrap long lines of text.",
7359                field: Box::new(SettingField {
7360                    json_path: Some("languages.$(language).soft_wrap"),
7361                    pick: |settings_content| {
7362                        language_settings_field(settings_content, |language| {
7363                            language.soft_wrap.as_ref()
7364                        })
7365                    },
7366                    write: |settings_content, value| {
7367                        language_settings_field_mut(settings_content, value, |language, value| {
7368                            language.soft_wrap = value;
7369                        })
7370                    },
7371                }),
7372                metadata: None,
7373                files: USER | PROJECT,
7374            }),
7375            SettingsPageItem::SettingItem(SettingItem {
7376                title: "Show Wrap Guides",
7377                description: "Show wrap guides in the editor.",
7378                field: Box::new(SettingField {
7379                    json_path: Some("languages.$(language).show_wrap_guides"),
7380                    pick: |settings_content| {
7381                        language_settings_field(settings_content, |language| {
7382                            language.show_wrap_guides.as_ref()
7383                        })
7384                    },
7385                    write: |settings_content, value| {
7386                        language_settings_field_mut(settings_content, value, |language, value| {
7387                            language.show_wrap_guides = value;
7388                        })
7389                    },
7390                }),
7391                metadata: None,
7392                files: USER | PROJECT,
7393            }),
7394            SettingsPageItem::SettingItem(SettingItem {
7395                title: "Preferred Line Length",
7396                description: "The column at which to soft-wrap lines, for buffers where soft-wrap is enabled.",
7397                field: Box::new(SettingField {
7398                    json_path: Some("languages.$(language).preferred_line_length"),
7399                    pick: |settings_content| {
7400                        language_settings_field(settings_content, |language| {
7401                            language.preferred_line_length.as_ref()
7402                        })
7403                    },
7404                    write: |settings_content, value| {
7405                        language_settings_field_mut(settings_content, value, |language, value| {
7406                            language.preferred_line_length = value;
7407                        })
7408                    },
7409                }),
7410                metadata: None,
7411                files: USER | PROJECT,
7412            }),
7413            SettingsPageItem::SettingItem(SettingItem {
7414                title: "Wrap Guides",
7415                description: "Character counts at which to show wrap guides in the editor.",
7416                field: Box::new(
7417                    SettingField {
7418                        json_path: Some("languages.$(language).wrap_guides"),
7419                        pick: |settings_content| {
7420                            language_settings_field(settings_content, |language| {
7421                                language.wrap_guides.as_ref()
7422                            })
7423                        },
7424                        write: |settings_content, value| {
7425                            language_settings_field_mut(
7426                                settings_content,
7427                                value,
7428                                |language, value| {
7429                                    language.wrap_guides = value;
7430                                },
7431                            )
7432                        },
7433                    }
7434                    .unimplemented(),
7435                ),
7436                metadata: None,
7437                files: USER | PROJECT,
7438            }),
7439            SettingsPageItem::SettingItem(SettingItem {
7440                title: "Allow Rewrap",
7441                description: "Controls where the `editor::rewrap` action is allowed for this language.",
7442                field: Box::new(SettingField {
7443                    json_path: Some("languages.$(language).allow_rewrap"),
7444                    pick: |settings_content| {
7445                        language_settings_field(settings_content, |language| {
7446                            language.allow_rewrap.as_ref()
7447                        })
7448                    },
7449                    write: |settings_content, value| {
7450                        language_settings_field_mut(settings_content, value, |language, value| {
7451                            language.allow_rewrap = value;
7452                        })
7453                    },
7454                }),
7455                metadata: None,
7456                files: USER | PROJECT,
7457            }),
7458        ]
7459    }
7460
7461    fn indent_guides_section() -> [SettingsPageItem; 6] {
7462        [
7463            SettingsPageItem::SectionHeader("Indent Guides"),
7464            SettingsPageItem::SettingItem(SettingItem {
7465                title: "Enabled",
7466                description: "Display indent guides in the editor.",
7467                field: Box::new(SettingField {
7468                    json_path: Some("languages.$(language).indent_guides.enabled"),
7469                    pick: |settings_content| {
7470                        language_settings_field(settings_content, |language| {
7471                            language
7472                                .indent_guides
7473                                .as_ref()
7474                                .and_then(|indent_guides| indent_guides.enabled.as_ref())
7475                        })
7476                    },
7477                    write: |settings_content, value| {
7478                        language_settings_field_mut(settings_content, value, |language, value| {
7479                            language.indent_guides.get_or_insert_default().enabled = value;
7480                        })
7481                    },
7482                }),
7483                metadata: None,
7484                files: USER | PROJECT,
7485            }),
7486            SettingsPageItem::SettingItem(SettingItem {
7487                title: "Line Width",
7488                description: "The width of the indent guides in pixels, between 1 and 10.",
7489                field: Box::new(SettingField {
7490                    json_path: Some("languages.$(language).indent_guides.line_width"),
7491                    pick: |settings_content| {
7492                        language_settings_field(settings_content, |language| {
7493                            language
7494                                .indent_guides
7495                                .as_ref()
7496                                .and_then(|indent_guides| indent_guides.line_width.as_ref())
7497                        })
7498                    },
7499                    write: |settings_content, value| {
7500                        language_settings_field_mut(settings_content, value, |language, value| {
7501                            language.indent_guides.get_or_insert_default().line_width = value;
7502                        })
7503                    },
7504                }),
7505                metadata: None,
7506                files: USER | PROJECT,
7507            }),
7508            SettingsPageItem::SettingItem(SettingItem {
7509                title: "Active Line Width",
7510                description: "The width of the active indent guide in pixels, between 1 and 10.",
7511                field: Box::new(SettingField {
7512                    json_path: Some("languages.$(language).indent_guides.active_line_width"),
7513                    pick: |settings_content| {
7514                        language_settings_field(settings_content, |language| {
7515                            language
7516                                .indent_guides
7517                                .as_ref()
7518                                .and_then(|indent_guides| indent_guides.active_line_width.as_ref())
7519                        })
7520                    },
7521                    write: |settings_content, value| {
7522                        language_settings_field_mut(settings_content, value, |language, value| {
7523                            language
7524                                .indent_guides
7525                                .get_or_insert_default()
7526                                .active_line_width = value;
7527                        })
7528                    },
7529                }),
7530                metadata: None,
7531                files: USER | PROJECT,
7532            }),
7533            SettingsPageItem::SettingItem(SettingItem {
7534                title: "Coloring",
7535                description: "Determines how indent guides are colored.",
7536                field: Box::new(SettingField {
7537                    json_path: Some("languages.$(language).indent_guides.coloring"),
7538                    pick: |settings_content| {
7539                        language_settings_field(settings_content, |language| {
7540                            language
7541                                .indent_guides
7542                                .as_ref()
7543                                .and_then(|indent_guides| indent_guides.coloring.as_ref())
7544                        })
7545                    },
7546                    write: |settings_content, value| {
7547                        language_settings_field_mut(settings_content, value, |language, value| {
7548                            language.indent_guides.get_or_insert_default().coloring = value;
7549                        })
7550                    },
7551                }),
7552                metadata: None,
7553                files: USER | PROJECT,
7554            }),
7555            SettingsPageItem::SettingItem(SettingItem {
7556                title: "Background Coloring",
7557                description: "Determines how indent guide backgrounds are colored.",
7558                field: Box::new(SettingField {
7559                    json_path: Some("languages.$(language).indent_guides.background_coloring"),
7560                    pick: |settings_content| {
7561                        language_settings_field(settings_content, |language| {
7562                            language.indent_guides.as_ref().and_then(|indent_guides| {
7563                                indent_guides.background_coloring.as_ref()
7564                            })
7565                        })
7566                    },
7567                    write: |settings_content, value| {
7568                        language_settings_field_mut(settings_content, value, |language, value| {
7569                            language
7570                                .indent_guides
7571                                .get_or_insert_default()
7572                                .background_coloring = value;
7573                        })
7574                    },
7575                }),
7576                metadata: None,
7577                files: USER | PROJECT,
7578            }),
7579        ]
7580    }
7581
7582    fn formatting_section() -> [SettingsPageItem; 7] {
7583        [
7584            SettingsPageItem::SectionHeader("Formatting"),
7585            SettingsPageItem::SettingItem(SettingItem {
7586                title: "Format On Save",
7587                description: "Whether or not to perform a buffer format before saving.",
7588                field: Box::new(
7589                    // TODO(settings_ui): this setting should just be a bool
7590                    SettingField {
7591                        json_path: Some("languages.$(language).format_on_save"),
7592                        pick: |settings_content| {
7593                            language_settings_field(settings_content, |language| {
7594                                language.format_on_save.as_ref()
7595                            })
7596                        },
7597                        write: |settings_content, value| {
7598                            language_settings_field_mut(
7599                                settings_content,
7600                                value,
7601                                |language, value| {
7602                                    language.format_on_save = value;
7603                                },
7604                            )
7605                        },
7606                    },
7607                ),
7608                metadata: None,
7609                files: USER | PROJECT,
7610            }),
7611            SettingsPageItem::SettingItem(SettingItem {
7612                title: "Remove Trailing Whitespace On Save",
7613                description: "Whether or not to remove any trailing whitespace from lines of a buffer before saving it.",
7614                field: Box::new(SettingField {
7615                    json_path: Some("languages.$(language).remove_trailing_whitespace_on_save"),
7616                    pick: |settings_content| {
7617                        language_settings_field(settings_content, |language| {
7618                            language.remove_trailing_whitespace_on_save.as_ref()
7619                        })
7620                    },
7621                    write: |settings_content, value| {
7622                        language_settings_field_mut(settings_content, value, |language, value| {
7623                            language.remove_trailing_whitespace_on_save = value;
7624                        })
7625                    },
7626                }),
7627                metadata: None,
7628                files: USER | PROJECT,
7629            }),
7630            SettingsPageItem::SettingItem(SettingItem {
7631                title: "Ensure Final Newline On Save",
7632                description: "Whether or not to ensure there's a single newline at the end of a buffer when saving it.",
7633                field: Box::new(SettingField {
7634                    json_path: Some("languages.$(language).ensure_final_newline_on_save"),
7635                    pick: |settings_content| {
7636                        language_settings_field(settings_content, |language| {
7637                            language.ensure_final_newline_on_save.as_ref()
7638                        })
7639                    },
7640                    write: |settings_content, value| {
7641                        language_settings_field_mut(settings_content, value, |language, value| {
7642                            language.ensure_final_newline_on_save = value;
7643                        })
7644                    },
7645                }),
7646                metadata: None,
7647                files: USER | PROJECT,
7648            }),
7649            SettingsPageItem::SettingItem(SettingItem {
7650                title: "Formatter",
7651                description: "How to perform a buffer format.",
7652                field: Box::new(
7653                    SettingField {
7654                        json_path: Some("languages.$(language).formatter"),
7655                        pick: |settings_content| {
7656                            language_settings_field(settings_content, |language| {
7657                                language.formatter.as_ref()
7658                            })
7659                        },
7660                        write: |settings_content, value| {
7661                            language_settings_field_mut(
7662                                settings_content,
7663                                value,
7664                                |language, value| {
7665                                    language.formatter = value;
7666                                },
7667                            )
7668                        },
7669                    }
7670                    .unimplemented(),
7671                ),
7672                metadata: None,
7673                files: USER | PROJECT,
7674            }),
7675            SettingsPageItem::SettingItem(SettingItem {
7676                title: "Use On Type Format",
7677                description: "Whether to use additional LSP queries to format (and amend) the code after every \"trigger\" symbol input, defined by LSP server capabilities",
7678                field: Box::new(SettingField {
7679                    json_path: Some("languages.$(language).use_on_type_format"),
7680                    pick: |settings_content| {
7681                        language_settings_field(settings_content, |language| {
7682                            language.use_on_type_format.as_ref()
7683                        })
7684                    },
7685                    write: |settings_content, value| {
7686                        language_settings_field_mut(settings_content, value, |language, value| {
7687                            language.use_on_type_format = value;
7688                        })
7689                    },
7690                }),
7691                metadata: None,
7692                files: USER | PROJECT,
7693            }),
7694            SettingsPageItem::SettingItem(SettingItem {
7695                title: "Code Actions On Format",
7696                description: "Additional code actions to run when formatting.",
7697                field: Box::new(
7698                    SettingField {
7699                        json_path: Some("languages.$(language).code_actions_on_format"),
7700                        pick: |settings_content| {
7701                            language_settings_field(settings_content, |language| {
7702                                language.code_actions_on_format.as_ref()
7703                            })
7704                        },
7705                        write: |settings_content, value| {
7706                            language_settings_field_mut(
7707                                settings_content,
7708                                value,
7709                                |language, value| {
7710                                    language.code_actions_on_format = value;
7711                                },
7712                            )
7713                        },
7714                    }
7715                    .unimplemented(),
7716                ),
7717                metadata: None,
7718                files: USER | PROJECT,
7719            }),
7720        ]
7721    }
7722
7723    fn autoclose_section() -> [SettingsPageItem; 5] {
7724        [
7725            SettingsPageItem::SectionHeader("Autoclose"),
7726            SettingsPageItem::SettingItem(SettingItem {
7727                title: "Use Autoclose",
7728                description: "Whether to automatically type closing characters for you. For example, when you type '(', Zed will automatically add a closing ')' at the correct position.",
7729                field: Box::new(SettingField {
7730                    json_path: Some("languages.$(language).use_autoclose"),
7731                    pick: |settings_content| {
7732                        language_settings_field(settings_content, |language| {
7733                            language.use_autoclose.as_ref()
7734                        })
7735                    },
7736                    write: |settings_content, value| {
7737                        language_settings_field_mut(settings_content, value, |language, value| {
7738                            language.use_autoclose = value;
7739                        })
7740                    },
7741                }),
7742                metadata: None,
7743                files: USER | PROJECT,
7744            }),
7745            SettingsPageItem::SettingItem(SettingItem {
7746                title: "Use Auto Surround",
7747                description: "Whether to automatically surround text with characters for you. For example, when you select text and type '(', Zed will automatically surround text with ().",
7748                field: Box::new(SettingField {
7749                    json_path: Some("languages.$(language).use_auto_surround"),
7750                    pick: |settings_content| {
7751                        language_settings_field(settings_content, |language| {
7752                            language.use_auto_surround.as_ref()
7753                        })
7754                    },
7755                    write: |settings_content, value| {
7756                        language_settings_field_mut(settings_content, value, |language, value| {
7757                            language.use_auto_surround = value;
7758                        })
7759                    },
7760                }),
7761                metadata: None,
7762                files: USER | PROJECT,
7763            }),
7764            SettingsPageItem::SettingItem(SettingItem {
7765                title: "Always Treat Brackets As Autoclosed",
7766                description: "Controls whether the closing characters are always skipped over and auto-removed no matter how they were inserted.",
7767                field: Box::new(SettingField {
7768                    json_path: Some("languages.$(language).always_treat_brackets_as_autoclosed"),
7769                    pick: |settings_content| {
7770                        language_settings_field(settings_content, |language| {
7771                            language.always_treat_brackets_as_autoclosed.as_ref()
7772                        })
7773                    },
7774                    write: |settings_content, value| {
7775                        language_settings_field_mut(settings_content, value, |language, value| {
7776                            language.always_treat_brackets_as_autoclosed = value;
7777                        })
7778                    },
7779                }),
7780                metadata: None,
7781                files: USER | PROJECT,
7782            }),
7783            SettingsPageItem::SettingItem(SettingItem {
7784                title: "JSX Tag Auto Close",
7785                description: "Whether to automatically close JSX tags.",
7786                field: Box::new(SettingField {
7787                    json_path: Some("languages.$(language).jsx_tag_auto_close"),
7788                    // TODO(settings_ui): this setting should just be a bool
7789                    pick: |settings_content| {
7790                        language_settings_field(settings_content, |language| {
7791                            language.jsx_tag_auto_close.as_ref()?.enabled.as_ref()
7792                        })
7793                    },
7794                    write: |settings_content, value| {
7795                        language_settings_field_mut(settings_content, value, |language, value| {
7796                            language.jsx_tag_auto_close.get_or_insert_default().enabled = value;
7797                        })
7798                    },
7799                }),
7800                metadata: None,
7801                files: USER | PROJECT,
7802            }),
7803        ]
7804    }
7805
7806    fn whitespace_section() -> [SettingsPageItem; 4] {
7807        [
7808            SettingsPageItem::SectionHeader("Whitespace"),
7809            SettingsPageItem::SettingItem(SettingItem {
7810                title: "Show Whitespaces",
7811                description: "Whether to show tabs and spaces in the editor.",
7812                field: Box::new(SettingField {
7813                    json_path: Some("languages.$(language).show_whitespaces"),
7814                    pick: |settings_content| {
7815                        language_settings_field(settings_content, |language| {
7816                            language.show_whitespaces.as_ref()
7817                        })
7818                    },
7819                    write: |settings_content, value| {
7820                        language_settings_field_mut(settings_content, value, |language, value| {
7821                            language.show_whitespaces = value;
7822                        })
7823                    },
7824                }),
7825                metadata: None,
7826                files: USER | PROJECT,
7827            }),
7828            SettingsPageItem::SettingItem(SettingItem {
7829                title: "Space Whitespace Indicator",
7830                description: "Visible character used to render space characters when show_whitespaces is enabled (default: \"\")",
7831                field: Box::new(
7832                    SettingField {
7833                        json_path: Some("languages.$(language).whitespace_map.space"),
7834                        pick: |settings_content| {
7835                            language_settings_field(settings_content, |language| {
7836                                language.whitespace_map.as_ref()?.space.as_ref()
7837                            })
7838                        },
7839                        write: |settings_content, value| {
7840                            language_settings_field_mut(
7841                                settings_content,
7842                                value,
7843                                |language, value| {
7844                                    language.whitespace_map.get_or_insert_default().space = value;
7845                                },
7846                            )
7847                        },
7848                    }
7849                    .unimplemented(),
7850                ),
7851                metadata: None,
7852                files: USER | PROJECT,
7853            }),
7854            SettingsPageItem::SettingItem(SettingItem {
7855                title: "Tab Whitespace Indicator",
7856                description: "Visible character used to render tab characters when show_whitespaces is enabled (default: \"\")",
7857                field: Box::new(
7858                    SettingField {
7859                        json_path: Some("languages.$(language).whitespace_map.tab"),
7860                        pick: |settings_content| {
7861                            language_settings_field(settings_content, |language| {
7862                                language.whitespace_map.as_ref()?.tab.as_ref()
7863                            })
7864                        },
7865                        write: |settings_content, value| {
7866                            language_settings_field_mut(
7867                                settings_content,
7868                                value,
7869                                |language, value| {
7870                                    language.whitespace_map.get_or_insert_default().tab = value;
7871                                },
7872                            )
7873                        },
7874                    }
7875                    .unimplemented(),
7876                ),
7877                metadata: None,
7878                files: USER | PROJECT,
7879            }),
7880        ]
7881    }
7882
7883    fn completions_section() -> [SettingsPageItem; 7] {
7884        [
7885            SettingsPageItem::SectionHeader("Completions"),
7886            SettingsPageItem::SettingItem(SettingItem {
7887                title: "Show Completions On Input",
7888                description: "Whether to pop the completions menu while typing in an editor without explicitly requesting it.",
7889                field: Box::new(SettingField {
7890                    json_path: Some("languages.$(language).show_completions_on_input"),
7891                    pick: |settings_content| {
7892                        language_settings_field(settings_content, |language| {
7893                            language.show_completions_on_input.as_ref()
7894                        })
7895                    },
7896                    write: |settings_content, value| {
7897                        language_settings_field_mut(settings_content, value, |language, value| {
7898                            language.show_completions_on_input = value;
7899                        })
7900                    },
7901                }),
7902                metadata: None,
7903                files: USER | PROJECT,
7904            }),
7905            SettingsPageItem::SettingItem(SettingItem {
7906                title: "Show Completion Documentation",
7907                description: "Whether to display inline and alongside documentation for items in the completions menu.",
7908                field: Box::new(SettingField {
7909                    json_path: Some("languages.$(language).show_completion_documentation"),
7910                    pick: |settings_content| {
7911                        language_settings_field(settings_content, |language| {
7912                            language.show_completion_documentation.as_ref()
7913                        })
7914                    },
7915                    write: |settings_content, value| {
7916                        language_settings_field_mut(settings_content, value, |language, value| {
7917                            language.show_completion_documentation = value;
7918                        })
7919                    },
7920                }),
7921                metadata: None,
7922                files: USER | PROJECT,
7923            }),
7924            SettingsPageItem::SettingItem(SettingItem {
7925                title: "Words",
7926                description: "Controls how words are completed.",
7927                field: Box::new(SettingField {
7928                    json_path: Some("languages.$(language).completions.words"),
7929                    pick: |settings_content| {
7930                        language_settings_field(settings_content, |language| {
7931                            language.completions.as_ref()?.words.as_ref()
7932                        })
7933                    },
7934                    write: |settings_content, value| {
7935                        language_settings_field_mut(settings_content, value, |language, value| {
7936                            language.completions.get_or_insert_default().words = value;
7937                        })
7938                    },
7939                }),
7940                metadata: None,
7941                files: USER | PROJECT,
7942            }),
7943            SettingsPageItem::SettingItem(SettingItem {
7944                title: "Words Min Length",
7945                description: "How many characters has to be in the completions query to automatically show the words-based completions.",
7946                field: Box::new(SettingField {
7947                    json_path: Some("languages.$(language).completions.words_min_length"),
7948                    pick: |settings_content| {
7949                        language_settings_field(settings_content, |language| {
7950                            language.completions.as_ref()?.words_min_length.as_ref()
7951                        })
7952                    },
7953                    write: |settings_content, value| {
7954                        language_settings_field_mut(settings_content, value, |language, value| {
7955                            language
7956                                .completions
7957                                .get_or_insert_default()
7958                                .words_min_length = value;
7959                        })
7960                    },
7961                }),
7962                metadata: None,
7963                files: USER | PROJECT,
7964            }),
7965            SettingsPageItem::SettingItem(SettingItem {
7966                title: "Completion Menu Scrollbar",
7967                description: "When to show the scrollbar in the completion menu.",
7968                field: Box::new(SettingField {
7969                    json_path: Some("editor.completion_menu_scrollbar"),
7970                    pick: |settings_content| {
7971                        settings_content.editor.completion_menu_scrollbar.as_ref()
7972                    },
7973                    write: |settings_content, value| {
7974                        settings_content.editor.completion_menu_scrollbar = value;
7975                    },
7976                }),
7977                metadata: None,
7978                files: USER,
7979            }),
7980            SettingsPageItem::SettingItem(SettingItem {
7981                title: "Completion Detail Alignment",
7982                description: "Whether to align detail text in code completions context menus left or right.",
7983                field: Box::new(SettingField {
7984                    json_path: Some("editor.completion_detail_alignment"),
7985                    pick: |settings_content| {
7986                        settings_content.editor.completion_detail_alignment.as_ref()
7987                    },
7988                    write: |settings_content, value| {
7989                        settings_content.editor.completion_detail_alignment = value;
7990                    },
7991                }),
7992                metadata: None,
7993                files: USER,
7994            }),
7995        ]
7996    }
7997
7998    fn inlay_hints_section() -> [SettingsPageItem; 10] {
7999        [
8000            SettingsPageItem::SectionHeader("Inlay Hints"),
8001            SettingsPageItem::SettingItem(SettingItem {
8002                title: "Enabled",
8003                description: "Global switch to toggle hints on and off.",
8004                field: Box::new(SettingField {
8005                    json_path: Some("languages.$(language).inlay_hints.enabled"),
8006                    pick: |settings_content| {
8007                        language_settings_field(settings_content, |language| {
8008                            language.inlay_hints.as_ref()?.enabled.as_ref()
8009                        })
8010                    },
8011                    write: |settings_content, value| {
8012                        language_settings_field_mut(settings_content, value, |language, value| {
8013                            language.inlay_hints.get_or_insert_default().enabled = value;
8014                        })
8015                    },
8016                }),
8017                metadata: None,
8018                files: USER | PROJECT,
8019            }),
8020            SettingsPageItem::SettingItem(SettingItem {
8021                title: "Show Value Hints",
8022                description: "Global switch to toggle inline values on and off when debugging.",
8023                field: Box::new(SettingField {
8024                    json_path: Some("languages.$(language).inlay_hints.show_value_hints"),
8025                    pick: |settings_content| {
8026                        language_settings_field(settings_content, |language| {
8027                            language.inlay_hints.as_ref()?.show_value_hints.as_ref()
8028                        })
8029                    },
8030                    write: |settings_content, value| {
8031                        language_settings_field_mut(settings_content, value, |language, value| {
8032                            language
8033                                .inlay_hints
8034                                .get_or_insert_default()
8035                                .show_value_hints = value;
8036                        })
8037                    },
8038                }),
8039                metadata: None,
8040                files: USER | PROJECT,
8041            }),
8042            SettingsPageItem::SettingItem(SettingItem {
8043                title: "Show Type Hints",
8044                description: "Whether type hints should be shown.",
8045                field: Box::new(SettingField {
8046                    json_path: Some("languages.$(language).inlay_hints.show_type_hints"),
8047                    pick: |settings_content| {
8048                        language_settings_field(settings_content, |language| {
8049                            language.inlay_hints.as_ref()?.show_type_hints.as_ref()
8050                        })
8051                    },
8052                    write: |settings_content, value| {
8053                        language_settings_field_mut(settings_content, value, |language, value| {
8054                            language.inlay_hints.get_or_insert_default().show_type_hints = value;
8055                        })
8056                    },
8057                }),
8058                metadata: None,
8059                files: USER | PROJECT,
8060            }),
8061            SettingsPageItem::SettingItem(SettingItem {
8062                title: "Show Parameter Hints",
8063                description: "Whether parameter hints should be shown.",
8064                field: Box::new(SettingField {
8065                    json_path: Some("languages.$(language).inlay_hints.show_parameter_hints"),
8066                    pick: |settings_content| {
8067                        language_settings_field(settings_content, |language| {
8068                            language.inlay_hints.as_ref()?.show_parameter_hints.as_ref()
8069                        })
8070                    },
8071                    write: |settings_content, value| {
8072                        language_settings_field_mut(settings_content, value, |language, value| {
8073                            language
8074                                .inlay_hints
8075                                .get_or_insert_default()
8076                                .show_parameter_hints = value;
8077                        })
8078                    },
8079                }),
8080                metadata: None,
8081                files: USER | PROJECT,
8082            }),
8083            SettingsPageItem::SettingItem(SettingItem {
8084                title: "Show Other Hints",
8085                description: "Whether other hints should be shown.",
8086                field: Box::new(SettingField {
8087                    json_path: Some("languages.$(language).inlay_hints.show_other_hints"),
8088                    pick: |settings_content| {
8089                        language_settings_field(settings_content, |language| {
8090                            language.inlay_hints.as_ref()?.show_other_hints.as_ref()
8091                        })
8092                    },
8093                    write: |settings_content, value| {
8094                        language_settings_field_mut(settings_content, value, |language, value| {
8095                            language
8096                                .inlay_hints
8097                                .get_or_insert_default()
8098                                .show_other_hints = value;
8099                        })
8100                    },
8101                }),
8102                metadata: None,
8103                files: USER | PROJECT,
8104            }),
8105            SettingsPageItem::SettingItem(SettingItem {
8106                title: "Show Background",
8107                description: "Show a background for inlay hints.",
8108                field: Box::new(SettingField {
8109                    json_path: Some("languages.$(language).inlay_hints.show_background"),
8110                    pick: |settings_content| {
8111                        language_settings_field(settings_content, |language| {
8112                            language.inlay_hints.as_ref()?.show_background.as_ref()
8113                        })
8114                    },
8115                    write: |settings_content, value| {
8116                        language_settings_field_mut(settings_content, value, |language, value| {
8117                            language.inlay_hints.get_or_insert_default().show_background = value;
8118                        })
8119                    },
8120                }),
8121                metadata: None,
8122                files: USER | PROJECT,
8123            }),
8124            SettingsPageItem::SettingItem(SettingItem {
8125                title: "Edit Debounce Ms",
8126                description: "Whether or not to debounce inlay hints updates after buffer edits (set to 0 to disable debouncing).",
8127                field: Box::new(SettingField {
8128                    json_path: Some("languages.$(language).inlay_hints.edit_debounce_ms"),
8129                    pick: |settings_content| {
8130                        language_settings_field(settings_content, |language| {
8131                            language.inlay_hints.as_ref()?.edit_debounce_ms.as_ref()
8132                        })
8133                    },
8134                    write: |settings_content, value| {
8135                        language_settings_field_mut(settings_content, value, |language, value| {
8136                            language
8137                                .inlay_hints
8138                                .get_or_insert_default()
8139                                .edit_debounce_ms = value;
8140                        })
8141                    },
8142                }),
8143                metadata: None,
8144                files: USER | PROJECT,
8145            }),
8146            SettingsPageItem::SettingItem(SettingItem {
8147                title: "Scroll Debounce Ms",
8148                description: "Whether or not to debounce inlay hints updates after buffer scrolls (set to 0 to disable debouncing).",
8149                field: Box::new(SettingField {
8150                    json_path: Some("languages.$(language).inlay_hints.scroll_debounce_ms"),
8151                    pick: |settings_content| {
8152                        language_settings_field(settings_content, |language| {
8153                            language.inlay_hints.as_ref()?.scroll_debounce_ms.as_ref()
8154                        })
8155                    },
8156                    write: |settings_content, value| {
8157                        language_settings_field_mut(settings_content, value, |language, value| {
8158                            language
8159                                .inlay_hints
8160                                .get_or_insert_default()
8161                                .scroll_debounce_ms = value;
8162                        })
8163                    },
8164                }),
8165                metadata: None,
8166                files: USER | PROJECT,
8167            }),
8168            SettingsPageItem::SettingItem(SettingItem {
8169                title: "Toggle On Modifiers Press",
8170                description: "Toggles inlay hints (hides or shows) when the user presses the modifiers specified.",
8171                field: Box::new(
8172                    SettingField {
8173                        json_path: Some(
8174                            "languages.$(language).inlay_hints.toggle_on_modifiers_press",
8175                        ),
8176                        pick: |settings_content| {
8177                            language_settings_field(settings_content, |language| {
8178                                language
8179                                    .inlay_hints
8180                                    .as_ref()?
8181                                    .toggle_on_modifiers_press
8182                                    .as_ref()
8183                            })
8184                        },
8185                        write: |settings_content, value| {
8186                            language_settings_field_mut(
8187                                settings_content,
8188                                value,
8189                                |language, value| {
8190                                    language
8191                                        .inlay_hints
8192                                        .get_or_insert_default()
8193                                        .toggle_on_modifiers_press = value;
8194                                },
8195                            )
8196                        },
8197                    }
8198                    .unimplemented(),
8199                ),
8200                metadata: None,
8201                files: USER | PROJECT,
8202            }),
8203        ]
8204    }
8205
8206    fn tasks_section() -> [SettingsPageItem; 4] {
8207        [
8208            SettingsPageItem::SectionHeader("Tasks"),
8209            SettingsPageItem::SettingItem(SettingItem {
8210                title: "Enabled",
8211                description: "Whether tasks are enabled for this language.",
8212                field: Box::new(SettingField {
8213                    json_path: Some("languages.$(language).tasks.enabled"),
8214                    pick: |settings_content| {
8215                        language_settings_field(settings_content, |language| {
8216                            language.tasks.as_ref()?.enabled.as_ref()
8217                        })
8218                    },
8219                    write: |settings_content, value| {
8220                        language_settings_field_mut(settings_content, value, |language, value| {
8221                            language.tasks.get_or_insert_default().enabled = value;
8222                        })
8223                    },
8224                }),
8225                metadata: None,
8226                files: USER | PROJECT,
8227            }),
8228            SettingsPageItem::SettingItem(SettingItem {
8229                title: "Variables",
8230                description: "Extra task variables to set for a particular language.",
8231                field: Box::new(
8232                    SettingField {
8233                        json_path: Some("languages.$(language).tasks.variables"),
8234                        pick: |settings_content| {
8235                            language_settings_field(settings_content, |language| {
8236                                language.tasks.as_ref()?.variables.as_ref()
8237                            })
8238                        },
8239                        write: |settings_content, value| {
8240                            language_settings_field_mut(
8241                                settings_content,
8242                                value,
8243                                |language, value| {
8244                                    language.tasks.get_or_insert_default().variables = value;
8245                                },
8246                            )
8247                        },
8248                    }
8249                    .unimplemented(),
8250                ),
8251                metadata: None,
8252                files: USER | PROJECT,
8253            }),
8254            SettingsPageItem::SettingItem(SettingItem {
8255                title: "Prefer LSP",
8256                description: "Use LSP tasks over Zed language extension tasks.",
8257                field: Box::new(SettingField {
8258                    json_path: Some("languages.$(language).tasks.prefer_lsp"),
8259                    pick: |settings_content| {
8260                        language_settings_field(settings_content, |language| {
8261                            language.tasks.as_ref()?.prefer_lsp.as_ref()
8262                        })
8263                    },
8264                    write: |settings_content, value| {
8265                        language_settings_field_mut(settings_content, value, |language, value| {
8266                            language.tasks.get_or_insert_default().prefer_lsp = value;
8267                        })
8268                    },
8269                }),
8270                metadata: None,
8271                files: USER | PROJECT,
8272            }),
8273        ]
8274    }
8275
8276    fn miscellaneous_section() -> [SettingsPageItem; 6] {
8277        [
8278            SettingsPageItem::SectionHeader("Miscellaneous"),
8279            SettingsPageItem::SettingItem(SettingItem {
8280                title: "Word Diff Enabled",
8281                description: "Whether to enable word diff highlighting in the editor. When enabled, changed words within modified lines are highlighted to show exactly what changed.",
8282                field: Box::new(SettingField {
8283                    json_path: Some("languages.$(language).word_diff_enabled"),
8284                    pick: |settings_content| {
8285                        language_settings_field(settings_content, |language| {
8286                            language.word_diff_enabled.as_ref()
8287                        })
8288                    },
8289                    write: |settings_content, value| {
8290                        language_settings_field_mut(settings_content, value, |language, value| {
8291                            language.word_diff_enabled = value;
8292                        })
8293                    },
8294                }),
8295                metadata: None,
8296                files: USER | PROJECT,
8297            }),
8298            SettingsPageItem::SettingItem(SettingItem {
8299                title: "Debuggers",
8300                description: "Preferred debuggers for this language.",
8301                field: Box::new(
8302                    SettingField {
8303                        json_path: Some("languages.$(language).debuggers"),
8304                        pick: |settings_content| {
8305                            language_settings_field(settings_content, |language| {
8306                                language.debuggers.as_ref()
8307                            })
8308                        },
8309                        write: |settings_content, value| {
8310                            language_settings_field_mut(
8311                                settings_content,
8312                                value,
8313                                |language, value| {
8314                                    language.debuggers = value;
8315                                },
8316                            )
8317                        },
8318                    }
8319                    .unimplemented(),
8320                ),
8321                metadata: None,
8322                files: USER | PROJECT,
8323            }),
8324            SettingsPageItem::SettingItem(SettingItem {
8325                title: "Middle Click Paste",
8326                description: "Enable middle-click paste on Linux.",
8327                field: Box::new(SettingField {
8328                    json_path: Some("languages.$(language).editor.middle_click_paste"),
8329                    pick: |settings_content| settings_content.editor.middle_click_paste.as_ref(),
8330                    write: |settings_content, value| {
8331                        settings_content.editor.middle_click_paste = value;
8332                    },
8333                }),
8334                metadata: None,
8335                files: USER,
8336            }),
8337            SettingsPageItem::SettingItem(SettingItem {
8338                title: "Extend Comment On Newline",
8339                description: "Whether to start a new line with a comment when a previous line is a comment as well.",
8340                field: Box::new(SettingField {
8341                    json_path: Some("languages.$(language).extend_comment_on_newline"),
8342                    pick: |settings_content| {
8343                        language_settings_field(settings_content, |language| {
8344                            language.extend_comment_on_newline.as_ref()
8345                        })
8346                    },
8347                    write: |settings_content, value| {
8348                        language_settings_field_mut(settings_content, value, |language, value| {
8349                            language.extend_comment_on_newline = value;
8350                        })
8351                    },
8352                }),
8353                metadata: None,
8354                files: USER | PROJECT,
8355            }),
8356            SettingsPageItem::SettingItem(SettingItem {
8357                title: "Colorize Brackets",
8358                description: "Whether to colorize brackets in the editor.",
8359                field: Box::new(SettingField {
8360                    json_path: Some("languages.$(language).colorize_brackets"),
8361                    pick: |settings_content| {
8362                        language_settings_field(settings_content, |language| {
8363                            language.colorize_brackets.as_ref()
8364                        })
8365                    },
8366                    write: |settings_content, value| {
8367                        language_settings_field_mut(settings_content, value, |language, value| {
8368                            language.colorize_brackets = value;
8369                        })
8370                    },
8371                }),
8372                metadata: None,
8373                files: USER | PROJECT,
8374            }),
8375        ]
8376    }
8377
8378    fn global_only_miscellaneous_sub_section() -> [SettingsPageItem; 3] {
8379        [
8380            SettingsPageItem::SettingItem(SettingItem {
8381                title: "Image Viewer",
8382                description: "The unit for image file sizes.",
8383                field: Box::new(SettingField {
8384                    json_path: Some("image_viewer.unit"),
8385                    pick: |settings_content| {
8386                        settings_content
8387                            .image_viewer
8388                            .as_ref()
8389                            .and_then(|image_viewer| image_viewer.unit.as_ref())
8390                    },
8391                    write: |settings_content, value| {
8392                        settings_content.image_viewer.get_or_insert_default().unit = value;
8393                    },
8394                }),
8395                metadata: None,
8396                files: USER,
8397            }),
8398            SettingsPageItem::SettingItem(SettingItem {
8399                title: "Auto Replace Emoji Shortcode",
8400                description: "Whether to automatically replace emoji shortcodes with emoji characters.",
8401                field: Box::new(SettingField {
8402                    json_path: Some("message_editor.auto_replace_emoji_shortcode"),
8403                    pick: |settings_content| {
8404                        settings_content
8405                            .message_editor
8406                            .as_ref()
8407                            .and_then(|message_editor| {
8408                                message_editor.auto_replace_emoji_shortcode.as_ref()
8409                            })
8410                    },
8411                    write: |settings_content, value| {
8412                        settings_content
8413                            .message_editor
8414                            .get_or_insert_default()
8415                            .auto_replace_emoji_shortcode = value;
8416                    },
8417                }),
8418                metadata: None,
8419                files: USER,
8420            }),
8421            SettingsPageItem::SettingItem(SettingItem {
8422                title: "Drop Size Target",
8423                description: "Relative size of the drop target in the editor that will open dropped file as a split pane.",
8424                field: Box::new(SettingField {
8425                    json_path: Some("drop_target_size"),
8426                    pick: |settings_content| settings_content.workspace.drop_target_size.as_ref(),
8427                    write: |settings_content, value| {
8428                        settings_content.workspace.drop_target_size = value;
8429                    },
8430                }),
8431                metadata: None,
8432                files: USER,
8433            }),
8434        ]
8435    }
8436
8437    let is_global = active_language().is_none();
8438
8439    let lsp_document_colors_item = [SettingsPageItem::SettingItem(SettingItem {
8440        title: "LSP Document Colors",
8441        description: "How to render LSP color previews in the editor.",
8442        field: Box::new(SettingField {
8443            json_path: Some("lsp_document_colors"),
8444            pick: |settings_content| settings_content.editor.lsp_document_colors.as_ref(),
8445            write: |settings_content, value| {
8446                settings_content.editor.lsp_document_colors = value;
8447            },
8448        }),
8449        metadata: None,
8450        files: USER,
8451    })];
8452
8453    if is_global {
8454        concat_sections!(
8455            indentation_section(),
8456            wrapping_section(),
8457            indent_guides_section(),
8458            formatting_section(),
8459            autoclose_section(),
8460            whitespace_section(),
8461            completions_section(),
8462            inlay_hints_section(),
8463            lsp_document_colors_item,
8464            tasks_section(),
8465            miscellaneous_section(),
8466            global_only_miscellaneous_sub_section(),
8467        )
8468    } else {
8469        concat_sections!(
8470            indentation_section(),
8471            wrapping_section(),
8472            indent_guides_section(),
8473            formatting_section(),
8474            autoclose_section(),
8475            whitespace_section(),
8476            completions_section(),
8477            inlay_hints_section(),
8478            tasks_section(),
8479            miscellaneous_section(),
8480        )
8481    }
8482}
8483
8484/// LanguageSettings items that should be included in the "Languages & Tools" page
8485/// not the "Editor" page
8486fn non_editor_language_settings_data() -> Box<[SettingsPageItem]> {
8487    fn lsp_section() -> [SettingsPageItem; 5] {
8488        [
8489            SettingsPageItem::SectionHeader("LSP"),
8490            SettingsPageItem::SettingItem(SettingItem {
8491                title: "Enable Language Server",
8492                description: "Whether to use language servers to provide code intelligence.",
8493                field: Box::new(SettingField {
8494                    json_path: Some("languages.$(language).enable_language_server"),
8495                    pick: |settings_content| {
8496                        language_settings_field(settings_content, |language| {
8497                            language.enable_language_server.as_ref()
8498                        })
8499                    },
8500                    write: |settings_content, value| {
8501                        language_settings_field_mut(settings_content, value, |language, value| {
8502                            language.enable_language_server = value;
8503                        })
8504                    },
8505                }),
8506                metadata: None,
8507                files: USER | PROJECT,
8508            }),
8509            SettingsPageItem::SettingItem(SettingItem {
8510                title: "Language Servers",
8511                description: "The list of language servers to use (or disable) for this language.",
8512                field: Box::new(
8513                    SettingField {
8514                        json_path: Some("languages.$(language).language_servers"),
8515                        pick: |settings_content| {
8516                            language_settings_field(settings_content, |language| {
8517                                language.language_servers.as_ref()
8518                            })
8519                        },
8520                        write: |settings_content, value| {
8521                            language_settings_field_mut(
8522                                settings_content,
8523                                value,
8524                                |language, value| {
8525                                    language.language_servers = value;
8526                                },
8527                            )
8528                        },
8529                    }
8530                    .unimplemented(),
8531                ),
8532                metadata: None,
8533                files: USER | PROJECT,
8534            }),
8535            SettingsPageItem::SettingItem(SettingItem {
8536                title: "Linked Edits",
8537                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.",
8538                field: Box::new(SettingField {
8539                    json_path: Some("languages.$(language).linked_edits"),
8540                    pick: |settings_content| {
8541                        language_settings_field(settings_content, |language| {
8542                            language.linked_edits.as_ref()
8543                        })
8544                    },
8545                    write: |settings_content, value| {
8546                        language_settings_field_mut(settings_content, value, |language, value| {
8547                            language.linked_edits = value;
8548                        })
8549                    },
8550                }),
8551                metadata: None,
8552                files: USER | PROJECT,
8553            }),
8554            SettingsPageItem::SettingItem(SettingItem {
8555                title: "Go To Definition Fallback",
8556                description: "Whether to follow-up empty Go to definition responses from the language server.",
8557                field: Box::new(SettingField {
8558                    json_path: Some("go_to_definition_fallback"),
8559                    pick: |settings_content| {
8560                        settings_content.editor.go_to_definition_fallback.as_ref()
8561                    },
8562                    write: |settings_content, value| {
8563                        settings_content.editor.go_to_definition_fallback = value;
8564                    },
8565                }),
8566                metadata: None,
8567                files: USER,
8568            }),
8569        ]
8570    }
8571
8572    fn lsp_completions_section() -> [SettingsPageItem; 4] {
8573        [
8574            SettingsPageItem::SectionHeader("LSP Completions"),
8575            SettingsPageItem::SettingItem(SettingItem {
8576                title: "Enabled",
8577                description: "Whether to fetch LSP completions or not.",
8578                field: Box::new(SettingField {
8579                    json_path: Some("languages.$(language).completions.lsp"),
8580                    pick: |settings_content| {
8581                        language_settings_field(settings_content, |language| {
8582                            language.completions.as_ref()?.lsp.as_ref()
8583                        })
8584                    },
8585                    write: |settings_content, value| {
8586                        language_settings_field_mut(settings_content, value, |language, value| {
8587                            language.completions.get_or_insert_default().lsp = value;
8588                        })
8589                    },
8590                }),
8591                metadata: None,
8592                files: USER | PROJECT,
8593            }),
8594            SettingsPageItem::SettingItem(SettingItem {
8595                title: "Fetch Timeout (milliseconds)",
8596                description: "When fetching LSP completions, determines how long to wait for a response of a particular server (set to 0 to wait indefinitely).",
8597                field: Box::new(SettingField {
8598                    json_path: Some("languages.$(language).completions.lsp_fetch_timeout_ms"),
8599                    pick: |settings_content| {
8600                        language_settings_field(settings_content, |language| {
8601                            language.completions.as_ref()?.lsp_fetch_timeout_ms.as_ref()
8602                        })
8603                    },
8604                    write: |settings_content, value| {
8605                        language_settings_field_mut(settings_content, value, |language, value| {
8606                            language
8607                                .completions
8608                                .get_or_insert_default()
8609                                .lsp_fetch_timeout_ms = value;
8610                        })
8611                    },
8612                }),
8613                metadata: None,
8614                files: USER | PROJECT,
8615            }),
8616            SettingsPageItem::SettingItem(SettingItem {
8617                title: "Insert Mode",
8618                description: "Controls how LSP completions are inserted.",
8619                field: Box::new(SettingField {
8620                    json_path: Some("languages.$(language).completions.lsp_insert_mode"),
8621                    pick: |settings_content| {
8622                        language_settings_field(settings_content, |language| {
8623                            language.completions.as_ref()?.lsp_insert_mode.as_ref()
8624                        })
8625                    },
8626                    write: |settings_content, value| {
8627                        language_settings_field_mut(settings_content, value, |language, value| {
8628                            language.completions.get_or_insert_default().lsp_insert_mode = value;
8629                        })
8630                    },
8631                }),
8632                metadata: None,
8633                files: USER | PROJECT,
8634            }),
8635        ]
8636    }
8637
8638    fn debugger_section() -> [SettingsPageItem; 2] {
8639        [
8640            SettingsPageItem::SectionHeader("Debuggers"),
8641            SettingsPageItem::SettingItem(SettingItem {
8642                title: "Debuggers",
8643                description: "Preferred debuggers for this language.",
8644                field: Box::new(
8645                    SettingField {
8646                        json_path: Some("languages.$(language).debuggers"),
8647                        pick: |settings_content| {
8648                            language_settings_field(settings_content, |language| {
8649                                language.debuggers.as_ref()
8650                            })
8651                        },
8652                        write: |settings_content, value| {
8653                            language_settings_field_mut(
8654                                settings_content,
8655                                value,
8656                                |language, value| {
8657                                    language.debuggers = value;
8658                                },
8659                            )
8660                        },
8661                    }
8662                    .unimplemented(),
8663                ),
8664                metadata: None,
8665                files: USER | PROJECT,
8666            }),
8667        ]
8668    }
8669
8670    fn prettier_section() -> [SettingsPageItem; 5] {
8671        [
8672            SettingsPageItem::SectionHeader("Prettier"),
8673            SettingsPageItem::SettingItem(SettingItem {
8674                title: "Allowed",
8675                description: "Enables or disables formatting with Prettier for a given language.",
8676                field: Box::new(SettingField {
8677                    json_path: Some("languages.$(language).prettier.allowed"),
8678                    pick: |settings_content| {
8679                        language_settings_field(settings_content, |language| {
8680                            language.prettier.as_ref()?.allowed.as_ref()
8681                        })
8682                    },
8683                    write: |settings_content, value| {
8684                        language_settings_field_mut(settings_content, value, |language, value| {
8685                            language.prettier.get_or_insert_default().allowed = value;
8686                        })
8687                    },
8688                }),
8689                metadata: None,
8690                files: USER | PROJECT,
8691            }),
8692            SettingsPageItem::SettingItem(SettingItem {
8693                title: "Parser",
8694                description: "Forces Prettier integration to use a specific parser name when formatting files with the language.",
8695                field: Box::new(SettingField {
8696                    json_path: Some("languages.$(language).prettier.parser"),
8697                    pick: |settings_content| {
8698                        language_settings_field(settings_content, |language| {
8699                            language.prettier.as_ref()?.parser.as_ref()
8700                        })
8701                    },
8702                    write: |settings_content, value| {
8703                        language_settings_field_mut(settings_content, value, |language, value| {
8704                            language.prettier.get_or_insert_default().parser = value;
8705                        })
8706                    },
8707                }),
8708                metadata: None,
8709                files: USER | PROJECT,
8710            }),
8711            SettingsPageItem::SettingItem(SettingItem {
8712                title: "Plugins",
8713                description: "Forces Prettier integration to use specific plugins when formatting files with the language.",
8714                field: Box::new(
8715                    SettingField {
8716                        json_path: Some("languages.$(language).prettier.plugins"),
8717                        pick: |settings_content| {
8718                            language_settings_field(settings_content, |language| {
8719                                language.prettier.as_ref()?.plugins.as_ref()
8720                            })
8721                        },
8722                        write: |settings_content, value| {
8723                            language_settings_field_mut(
8724                                settings_content,
8725                                value,
8726                                |language, value| {
8727                                    language.prettier.get_or_insert_default().plugins = value;
8728                                },
8729                            )
8730                        },
8731                    }
8732                    .unimplemented(),
8733                ),
8734                metadata: None,
8735                files: USER | PROJECT,
8736            }),
8737            SettingsPageItem::SettingItem(SettingItem {
8738                title: "Options",
8739                description: "Default Prettier options, in the format as in package.json section for Prettier.",
8740                field: Box::new(
8741                    SettingField {
8742                        json_path: Some("languages.$(language).prettier.options"),
8743                        pick: |settings_content| {
8744                            language_settings_field(settings_content, |language| {
8745                                language.prettier.as_ref()?.options.as_ref()
8746                            })
8747                        },
8748                        write: |settings_content, value| {
8749                            language_settings_field_mut(
8750                                settings_content,
8751                                value,
8752                                |language, value| {
8753                                    language.prettier.get_or_insert_default().options = value;
8754                                },
8755                            )
8756                        },
8757                    }
8758                    .unimplemented(),
8759                ),
8760                metadata: None,
8761                files: USER | PROJECT,
8762            }),
8763        ]
8764    }
8765
8766    concat_sections!(
8767        lsp_section(),
8768        lsp_completions_section(),
8769        debugger_section(),
8770        prettier_section(),
8771    )
8772}
8773
8774fn edit_prediction_language_settings_section() -> [SettingsPageItem; 4] {
8775    [
8776        SettingsPageItem::SectionHeader("Edit Predictions"),
8777        SettingsPageItem::SubPageLink(SubPageLink {
8778            title: "Configure Providers".into(),
8779            r#type: Default::default(),
8780            json_path: Some("edit_predictions.providers"),
8781            description: Some("Set up different edit prediction providers in complement to Zed's built-in Zeta model.".into()),
8782            in_json: false,
8783            files: USER,
8784            render: render_edit_prediction_setup_page
8785        }),
8786        SettingsPageItem::SettingItem(SettingItem {
8787            title: "Show Edit Predictions",
8788            description: "Controls whether edit predictions are shown immediately or manually.",
8789            field: Box::new(SettingField {
8790                json_path: Some("languages.$(language).show_edit_predictions"),
8791                pick: |settings_content| {
8792                    language_settings_field(settings_content, |language| {
8793                        language.show_edit_predictions.as_ref()
8794                    })
8795                },
8796                write: |settings_content, value| {
8797                    language_settings_field_mut(settings_content, value, |language, value| {
8798                        language.show_edit_predictions = value;
8799                    })
8800                },
8801            }),
8802            metadata: None,
8803            files: USER | PROJECT,
8804        }),
8805        SettingsPageItem::SettingItem(SettingItem {
8806            title: "Disable in Language Scopes",
8807            description: "Controls whether edit predictions are shown in the given language scopes.",
8808            field: Box::new(
8809                SettingField {
8810                    json_path: Some("languages.$(language).edit_predictions_disabled_in"),
8811                    pick: |settings_content| {
8812                        language_settings_field(settings_content, |language| {
8813                            language.edit_predictions_disabled_in.as_ref()
8814                        })
8815                    },
8816                    write: |settings_content, value| {
8817                        language_settings_field_mut(settings_content, value, |language, value| {
8818                            language.edit_predictions_disabled_in = value;
8819                        })
8820                    },
8821                }
8822                .unimplemented(),
8823            ),
8824            metadata: None,
8825            files: USER | PROJECT,
8826        }),
8827    ]
8828}
8829
8830fn show_scrollbar_or_editor(
8831    settings_content: &SettingsContent,
8832    show: fn(&SettingsContent) -> Option<&settings::ShowScrollbar>,
8833) -> Option<&settings::ShowScrollbar> {
8834    show(settings_content).or(settings_content
8835        .editor
8836        .scrollbar
8837        .as_ref()
8838        .and_then(|scrollbar| scrollbar.show.as_ref()))
8839}
8840
8841fn dynamic_variants<T>() -> &'static [T::Discriminant]
8842where
8843    T: strum::IntoDiscriminant,
8844    T::Discriminant: strum::VariantArray,
8845{
8846    <<T as strum::IntoDiscriminant>::Discriminant as strum::VariantArray>::VARIANTS
8847}