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::CurrentFileDirectory => {
5766                                        settings::WorkingDirectory::CurrentFileDirectory
5767                                    },
5768                                    settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => {
5769                                        settings::WorkingDirectory::CurrentProjectDirectory
5770                                    }
5771                                    settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => {
5772                                        settings::WorkingDirectory::FirstProjectDirectory
5773                                    }
5774                                    settings::WorkingDirectoryDiscriminants::AlwaysHome => {
5775                                        settings::WorkingDirectory::AlwaysHome
5776                                    }
5777                                    settings::WorkingDirectoryDiscriminants::Always => {
5778                                        let directory = match settings_value {
5779                                            settings::WorkingDirectory::Always { .. } => return,
5780                                            _ => String::new(),
5781                                        };
5782                                        settings::WorkingDirectory::Always { directory }
5783                                    }
5784                                };
5785                            },
5786                        }),
5787                        metadata: None,
5788                    },
5789                    pick_discriminant: |settings_content| {
5790                        Some(
5791                            settings_content
5792                                .terminal
5793                                .as_ref()?
5794                                .project
5795                                .working_directory
5796                                .as_ref()?
5797                                .discriminant() as usize,
5798                        )
5799                    },
5800                    fields: dynamic_variants::<settings::WorkingDirectory>()
5801                        .into_iter()
5802                        .map(|variant| match variant {
5803                            settings::WorkingDirectoryDiscriminants::CurrentFileDirectory => vec![],
5804                            settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => vec![],
5805                            settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => vec![],
5806                            settings::WorkingDirectoryDiscriminants::AlwaysHome => vec![],
5807                            settings::WorkingDirectoryDiscriminants::Always => vec![SettingItem {
5808                                files: USER | PROJECT,
5809                                title: "Directory",
5810                                description: "The directory path to use (will be shell expanded).",
5811                                field: Box::new(SettingField {
5812                                    json_path: Some("terminal.working_directory.always"),
5813                                    pick: |settings_content| {
5814                                        match settings_content.terminal.as_ref()?.project.working_directory.as_ref() {
5815                                            Some(settings::WorkingDirectory::Always { directory }) => Some(directory),
5816                                            _ => None,
5817                                        }
5818                                    },
5819                                    write: |settings_content, value| {
5820                                        let value = value.unwrap_or_default();
5821                                        match settings_content
5822                                            .terminal
5823                                            .get_or_insert_default()
5824                                            .project
5825                                            .working_directory
5826                                            .as_mut()
5827                                        {
5828                                            Some(settings::WorkingDirectory::Always { directory }) => *directory = value,
5829                                            _ => return,
5830                                        }
5831                                    },
5832                                }),
5833                                metadata: None,
5834                            }],
5835                        })
5836                        .collect(),
5837                }),
5838                SettingsPageItem::SettingItem(SettingItem {
5839                    title: "Environment Variables",
5840                    description: "Key-value pairs to add to the terminal's environment.",
5841                    field: Box::new(
5842                        SettingField {
5843                            json_path: Some("terminal.env"),
5844                            pick: |settings_content| settings_content.terminal.as_ref()?.project.env.as_ref(),
5845                            write: |settings_content, value| {
5846                                settings_content.terminal.get_or_insert_default().project.env = value;
5847                            },
5848                        }
5849                        .unimplemented(),
5850                    ),
5851                    metadata: None,
5852                    files: USER | PROJECT,
5853                }),
5854                SettingsPageItem::SettingItem(SettingItem {
5855                    title: "Detect Virtual Environment",
5856                    description: "Activates the Python virtual environment, if one is found, in the terminal's working directory.",
5857                    field: Box::new(
5858                        SettingField {
5859                            json_path: Some("terminal.detect_venv"),
5860                            pick: |settings_content| settings_content.terminal.as_ref()?.project.detect_venv.as_ref(),
5861                            write: |settings_content, value| {
5862                                settings_content
5863                                    .terminal
5864                                    .get_or_insert_default()
5865                                    .project
5866                                    .detect_venv = value;
5867                            },
5868                        }
5869                        .unimplemented(),
5870                    ),
5871                    metadata: None,
5872                    files: USER | PROJECT,
5873                }),
5874            ]
5875    }
5876
5877    fn font_section() -> [SettingsPageItem; 6] {
5878        [
5879            SettingsPageItem::SectionHeader("Font"),
5880            SettingsPageItem::SettingItem(SettingItem {
5881                title: "Font Size",
5882                description: "Font size for terminal text. If not set, defaults to buffer font size.",
5883                field: Box::new(SettingField {
5884                    json_path: Some("terminal.font_size"),
5885                    pick: |settings_content| {
5886                        settings_content
5887                            .terminal
5888                            .as_ref()
5889                            .and_then(|terminal| terminal.font_size.as_ref())
5890                            .or(settings_content.theme.buffer_font_size.as_ref())
5891                    },
5892                    write: |settings_content, value| {
5893                        settings_content.terminal.get_or_insert_default().font_size = value;
5894                    },
5895                }),
5896                metadata: None,
5897                files: USER,
5898            }),
5899            SettingsPageItem::SettingItem(SettingItem {
5900                title: "Font Family",
5901                description: "Font family for terminal text. If not set, defaults to buffer font family.",
5902                field: Box::new(SettingField {
5903                    json_path: Some("terminal.font_family"),
5904                    pick: |settings_content| {
5905                        settings_content
5906                            .terminal
5907                            .as_ref()
5908                            .and_then(|terminal| terminal.font_family.as_ref())
5909                            .or(settings_content.theme.buffer_font_family.as_ref())
5910                    },
5911                    write: |settings_content, value| {
5912                        settings_content
5913                            .terminal
5914                            .get_or_insert_default()
5915                            .font_family = value;
5916                    },
5917                }),
5918                metadata: None,
5919                files: USER,
5920            }),
5921            SettingsPageItem::SettingItem(SettingItem {
5922                title: "Font Fallbacks",
5923                description: "Font fallbacks for terminal text. If not set, defaults to buffer font fallbacks.",
5924                field: Box::new(
5925                    SettingField {
5926                        json_path: Some("terminal.font_fallbacks"),
5927                        pick: |settings_content| {
5928                            settings_content
5929                                .terminal
5930                                .as_ref()
5931                                .and_then(|terminal| terminal.font_fallbacks.as_ref())
5932                                .or(settings_content.theme.buffer_font_fallbacks.as_ref())
5933                        },
5934                        write: |settings_content, value| {
5935                            settings_content
5936                                .terminal
5937                                .get_or_insert_default()
5938                                .font_fallbacks = value;
5939                        },
5940                    }
5941                    .unimplemented(),
5942                ),
5943                metadata: None,
5944                files: USER,
5945            }),
5946            SettingsPageItem::SettingItem(SettingItem {
5947                title: "Font Weight",
5948                description: "Font weight for terminal text in CSS weight units (100-900).",
5949                field: Box::new(SettingField {
5950                    json_path: Some("terminal.font_weight"),
5951                    pick: |settings_content| {
5952                        settings_content.terminal.as_ref()?.font_weight.as_ref()
5953                    },
5954                    write: |settings_content, value| {
5955                        settings_content
5956                            .terminal
5957                            .get_or_insert_default()
5958                            .font_weight = value;
5959                    },
5960                }),
5961                metadata: None,
5962                files: USER,
5963            }),
5964            SettingsPageItem::SettingItem(SettingItem {
5965                title: "Font Features",
5966                description: "Font features for terminal text.",
5967                field: Box::new(
5968                    SettingField {
5969                        json_path: Some("terminal.font_features"),
5970                        pick: |settings_content| {
5971                            settings_content
5972                                .terminal
5973                                .as_ref()
5974                                .and_then(|terminal| terminal.font_features.as_ref())
5975                                .or(settings_content.theme.buffer_font_features.as_ref())
5976                        },
5977                        write: |settings_content, value| {
5978                            settings_content
5979                                .terminal
5980                                .get_or_insert_default()
5981                                .font_features = value;
5982                        },
5983                    }
5984                    .unimplemented(),
5985                ),
5986                metadata: None,
5987                files: USER,
5988            }),
5989        ]
5990    }
5991
5992    fn display_settings_section() -> [SettingsPageItem; 6] {
5993        [
5994            SettingsPageItem::SectionHeader("Display Settings"),
5995            SettingsPageItem::SettingItem(SettingItem {
5996                title: "Line Height",
5997                description: "Line height for terminal text.",
5998                field: Box::new(
5999                    SettingField {
6000                        json_path: Some("terminal.line_height"),
6001                        pick: |settings_content| {
6002                            settings_content.terminal.as_ref()?.line_height.as_ref()
6003                        },
6004                        write: |settings_content, value| {
6005                            settings_content
6006                                .terminal
6007                                .get_or_insert_default()
6008                                .line_height = value;
6009                        },
6010                    }
6011                    .unimplemented(),
6012                ),
6013                metadata: None,
6014                files: USER,
6015            }),
6016            SettingsPageItem::SettingItem(SettingItem {
6017                title: "Cursor Shape",
6018                description: "Default cursor shape for the terminal (bar, block, underline, or hollow).",
6019                field: Box::new(SettingField {
6020                    json_path: Some("terminal.cursor_shape"),
6021                    pick: |settings_content| {
6022                        settings_content.terminal.as_ref()?.cursor_shape.as_ref()
6023                    },
6024                    write: |settings_content, value| {
6025                        settings_content
6026                            .terminal
6027                            .get_or_insert_default()
6028                            .cursor_shape = value;
6029                    },
6030                }),
6031                metadata: None,
6032                files: USER,
6033            }),
6034            SettingsPageItem::SettingItem(SettingItem {
6035                title: "Cursor Blinking",
6036                description: "Sets the cursor blinking behavior in the terminal.",
6037                field: Box::new(SettingField {
6038                    json_path: Some("terminal.blinking"),
6039                    pick: |settings_content| settings_content.terminal.as_ref()?.blinking.as_ref(),
6040                    write: |settings_content, value| {
6041                        settings_content.terminal.get_or_insert_default().blinking = value;
6042                    },
6043                }),
6044                metadata: None,
6045                files: USER,
6046            }),
6047            SettingsPageItem::SettingItem(SettingItem {
6048                title: "Alternate Scroll",
6049                description: "Whether alternate scroll mode is active by default (converts mouse scroll to arrow keys in apps like Vim).",
6050                field: Box::new(SettingField {
6051                    json_path: Some("terminal.alternate_scroll"),
6052                    pick: |settings_content| {
6053                        settings_content
6054                            .terminal
6055                            .as_ref()?
6056                            .alternate_scroll
6057                            .as_ref()
6058                    },
6059                    write: |settings_content, value| {
6060                        settings_content
6061                            .terminal
6062                            .get_or_insert_default()
6063                            .alternate_scroll = value;
6064                    },
6065                }),
6066                metadata: None,
6067                files: USER,
6068            }),
6069            SettingsPageItem::SettingItem(SettingItem {
6070                title: "Minimum Contrast",
6071                description: "The minimum APCA perceptual contrast between foreground and background colors (0-106).",
6072                field: Box::new(SettingField {
6073                    json_path: Some("terminal.minimum_contrast"),
6074                    pick: |settings_content| {
6075                        settings_content
6076                            .terminal
6077                            .as_ref()?
6078                            .minimum_contrast
6079                            .as_ref()
6080                    },
6081                    write: |settings_content, value| {
6082                        settings_content
6083                            .terminal
6084                            .get_or_insert_default()
6085                            .minimum_contrast = value;
6086                    },
6087                }),
6088                metadata: None,
6089                files: USER,
6090            }),
6091        ]
6092    }
6093
6094    fn behavior_settings_section() -> [SettingsPageItem; 4] {
6095        [
6096            SettingsPageItem::SectionHeader("Behavior Settings"),
6097            SettingsPageItem::SettingItem(SettingItem {
6098                title: "Option As Meta",
6099                description: "Whether the option key behaves as the meta key.",
6100                field: Box::new(SettingField {
6101                    json_path: Some("terminal.option_as_meta"),
6102                    pick: |settings_content| {
6103                        settings_content.terminal.as_ref()?.option_as_meta.as_ref()
6104                    },
6105                    write: |settings_content, value| {
6106                        settings_content
6107                            .terminal
6108                            .get_or_insert_default()
6109                            .option_as_meta = value;
6110                    },
6111                }),
6112                metadata: None,
6113                files: USER,
6114            }),
6115            SettingsPageItem::SettingItem(SettingItem {
6116                title: "Copy On Select",
6117                description: "Whether selecting text in the terminal automatically copies to the system clipboard.",
6118                field: Box::new(SettingField {
6119                    json_path: Some("terminal.copy_on_select"),
6120                    pick: |settings_content| {
6121                        settings_content.terminal.as_ref()?.copy_on_select.as_ref()
6122                    },
6123                    write: |settings_content, value| {
6124                        settings_content
6125                            .terminal
6126                            .get_or_insert_default()
6127                            .copy_on_select = value;
6128                    },
6129                }),
6130                metadata: None,
6131                files: USER,
6132            }),
6133            SettingsPageItem::SettingItem(SettingItem {
6134                title: "Keep Selection On Copy",
6135                description: "Whether to keep the text selection after copying it to the clipboard.",
6136                field: Box::new(SettingField {
6137                    json_path: Some("terminal.keep_selection_on_copy"),
6138                    pick: |settings_content| {
6139                        settings_content
6140                            .terminal
6141                            .as_ref()?
6142                            .keep_selection_on_copy
6143                            .as_ref()
6144                    },
6145                    write: |settings_content, value| {
6146                        settings_content
6147                            .terminal
6148                            .get_or_insert_default()
6149                            .keep_selection_on_copy = value;
6150                    },
6151                }),
6152                metadata: None,
6153                files: USER,
6154            }),
6155        ]
6156    }
6157
6158    fn layout_settings_section() -> [SettingsPageItem; 3] {
6159        [
6160            SettingsPageItem::SectionHeader("Layout Settings"),
6161            SettingsPageItem::SettingItem(SettingItem {
6162                title: "Default Width",
6163                description: "Default width when the terminal is docked to the left or right (in pixels).",
6164                field: Box::new(SettingField {
6165                    json_path: Some("terminal.default_width"),
6166                    pick: |settings_content| {
6167                        settings_content.terminal.as_ref()?.default_width.as_ref()
6168                    },
6169                    write: |settings_content, value| {
6170                        settings_content
6171                            .terminal
6172                            .get_or_insert_default()
6173                            .default_width = value;
6174                    },
6175                }),
6176                metadata: None,
6177                files: USER,
6178            }),
6179            SettingsPageItem::SettingItem(SettingItem {
6180                title: "Default Height",
6181                description: "Default height when the terminal is docked to the bottom (in pixels).",
6182                field: Box::new(SettingField {
6183                    json_path: Some("terminal.default_height"),
6184                    pick: |settings_content| {
6185                        settings_content.terminal.as_ref()?.default_height.as_ref()
6186                    },
6187                    write: |settings_content, value| {
6188                        settings_content
6189                            .terminal
6190                            .get_or_insert_default()
6191                            .default_height = value;
6192                    },
6193                }),
6194                metadata: None,
6195                files: USER,
6196            }),
6197        ]
6198    }
6199
6200    fn advanced_settings_section() -> [SettingsPageItem; 3] {
6201        [
6202            SettingsPageItem::SectionHeader("Advanced Settings"),
6203            SettingsPageItem::SettingItem(SettingItem {
6204                title: "Max Scroll History Lines",
6205                description: "Maximum number of lines to keep in scrollback history (max: 100,000; 0 disables scrolling).",
6206                field: Box::new(SettingField {
6207                    json_path: Some("terminal.max_scroll_history_lines"),
6208                    pick: |settings_content| {
6209                        settings_content
6210                            .terminal
6211                            .as_ref()?
6212                            .max_scroll_history_lines
6213                            .as_ref()
6214                    },
6215                    write: |settings_content, value| {
6216                        settings_content
6217                            .terminal
6218                            .get_or_insert_default()
6219                            .max_scroll_history_lines = value;
6220                    },
6221                }),
6222                metadata: None,
6223                files: USER,
6224            }),
6225            SettingsPageItem::SettingItem(SettingItem {
6226                title: "Scroll Multiplier",
6227                description: "The multiplier for scrolling in the terminal with the mouse wheel",
6228                field: Box::new(SettingField {
6229                    json_path: Some("terminal.scroll_multiplier"),
6230                    pick: |settings_content| {
6231                        settings_content
6232                            .terminal
6233                            .as_ref()?
6234                            .scroll_multiplier
6235                            .as_ref()
6236                    },
6237                    write: |settings_content, value| {
6238                        settings_content
6239                            .terminal
6240                            .get_or_insert_default()
6241                            .scroll_multiplier = value;
6242                    },
6243                }),
6244                metadata: None,
6245                files: USER,
6246            }),
6247        ]
6248    }
6249
6250    fn toolbar_section() -> [SettingsPageItem; 2] {
6251        [
6252            SettingsPageItem::SectionHeader("Toolbar"),
6253            SettingsPageItem::SettingItem(SettingItem {
6254                title: "Breadcrumbs",
6255                description: "Display the terminal title in breadcrumbs inside the terminal pane.",
6256                field: Box::new(SettingField {
6257                    json_path: Some("terminal.toolbar.breadcrumbs"),
6258                    pick: |settings_content| {
6259                        settings_content
6260                            .terminal
6261                            .as_ref()?
6262                            .toolbar
6263                            .as_ref()?
6264                            .breadcrumbs
6265                            .as_ref()
6266                    },
6267                    write: |settings_content, value| {
6268                        settings_content
6269                            .terminal
6270                            .get_or_insert_default()
6271                            .toolbar
6272                            .get_or_insert_default()
6273                            .breadcrumbs = value;
6274                    },
6275                }),
6276                metadata: None,
6277                files: USER,
6278            }),
6279        ]
6280    }
6281
6282    fn scrollbar_section() -> [SettingsPageItem; 2] {
6283        [
6284            SettingsPageItem::SectionHeader("Scrollbar"),
6285            SettingsPageItem::SettingItem(SettingItem {
6286                title: "Show Scrollbar",
6287                description: "When to show the scrollbar in the terminal.",
6288                field: Box::new(SettingField {
6289                    json_path: Some("terminal.scrollbar.show"),
6290                    pick: |settings_content| {
6291                        show_scrollbar_or_editor(settings_content, |settings_content| {
6292                            settings_content
6293                                .terminal
6294                                .as_ref()?
6295                                .scrollbar
6296                                .as_ref()?
6297                                .show
6298                                .as_ref()
6299                        })
6300                    },
6301                    write: |settings_content, value| {
6302                        settings_content
6303                            .terminal
6304                            .get_or_insert_default()
6305                            .scrollbar
6306                            .get_or_insert_default()
6307                            .show = value;
6308                    },
6309                }),
6310                metadata: None,
6311                files: USER,
6312            }),
6313        ]
6314    }
6315
6316    SettingsPage {
6317        title: "Terminal",
6318        items: concat_sections![
6319            environment_section(),
6320            font_section(),
6321            display_settings_section(),
6322            behavior_settings_section(),
6323            layout_settings_section(),
6324            advanced_settings_section(),
6325            toolbar_section(),
6326            scrollbar_section(),
6327        ],
6328    }
6329}
6330
6331fn version_control_page() -> SettingsPage {
6332    fn git_integration_section() -> [SettingsPageItem; 2] {
6333        [
6334            SettingsPageItem::SectionHeader("Git Integration"),
6335            SettingsPageItem::DynamicItem(DynamicItem {
6336                discriminant: SettingItem {
6337                    files: USER,
6338                    title: "Disable Git Integration",
6339                    description: "Disable all Git integration features in Zed.",
6340                    field: Box::new(SettingField::<bool> {
6341                        json_path: Some("git.disable_git"),
6342                        pick: |settings_content| {
6343                            settings_content
6344                                .git
6345                                .as_ref()?
6346                                .enabled
6347                                .as_ref()?
6348                                .disable_git
6349                                .as_ref()
6350                        },
6351                        write: |settings_content, value| {
6352                            settings_content
6353                                .git
6354                                .get_or_insert_default()
6355                                .enabled
6356                                .get_or_insert_default()
6357                                .disable_git = value;
6358                        },
6359                    }),
6360                    metadata: None,
6361                },
6362                pick_discriminant: |settings_content| {
6363                    let disabled = settings_content
6364                        .git
6365                        .as_ref()?
6366                        .enabled
6367                        .as_ref()?
6368                        .disable_git
6369                        .unwrap_or(false);
6370                    Some(if disabled { 0 } else { 1 })
6371                },
6372                fields: vec![
6373                    vec![],
6374                    vec![
6375                        SettingItem {
6376                            files: USER,
6377                            title: "Enable Git Status",
6378                            description: "Show Git status information in the editor.",
6379                            field: Box::new(SettingField::<bool> {
6380                                json_path: Some("git.enable_status"),
6381                                pick: |settings_content| {
6382                                    settings_content
6383                                        .git
6384                                        .as_ref()?
6385                                        .enabled
6386                                        .as_ref()?
6387                                        .enable_status
6388                                        .as_ref()
6389                                },
6390                                write: |settings_content, value| {
6391                                    settings_content
6392                                        .git
6393                                        .get_or_insert_default()
6394                                        .enabled
6395                                        .get_or_insert_default()
6396                                        .enable_status = value;
6397                                },
6398                            }),
6399                            metadata: None,
6400                        },
6401                        SettingItem {
6402                            files: USER,
6403                            title: "Enable Git Diff",
6404                            description: "Show Git diff information in the editor.",
6405                            field: Box::new(SettingField::<bool> {
6406                                json_path: Some("git.enable_diff"),
6407                                pick: |settings_content| {
6408                                    settings_content
6409                                        .git
6410                                        .as_ref()?
6411                                        .enabled
6412                                        .as_ref()?
6413                                        .enable_diff
6414                                        .as_ref()
6415                                },
6416                                write: |settings_content, value| {
6417                                    settings_content
6418                                        .git
6419                                        .get_or_insert_default()
6420                                        .enabled
6421                                        .get_or_insert_default()
6422                                        .enable_diff = value;
6423                                },
6424                            }),
6425                            metadata: None,
6426                        },
6427                    ],
6428                ],
6429            }),
6430        ]
6431    }
6432
6433    fn git_gutter_section() -> [SettingsPageItem; 3] {
6434        [
6435            SettingsPageItem::SectionHeader("Git Gutter"),
6436            SettingsPageItem::SettingItem(SettingItem {
6437                title: "Visibility",
6438                description: "Control whether Git status is shown in the editor's gutter.",
6439                field: Box::new(SettingField {
6440                    json_path: Some("git.git_gutter"),
6441                    pick: |settings_content| settings_content.git.as_ref()?.git_gutter.as_ref(),
6442                    write: |settings_content, value| {
6443                        settings_content.git.get_or_insert_default().git_gutter = value;
6444                    },
6445                }),
6446                metadata: None,
6447                files: USER,
6448            }),
6449            // todo(settings_ui): Figure out the right default for this value in default.json
6450            SettingsPageItem::SettingItem(SettingItem {
6451                title: "Debounce",
6452                description: "Debounce threshold in milliseconds after which changes are reflected in the Git gutter.",
6453                field: Box::new(SettingField {
6454                    json_path: Some("git.gutter_debounce"),
6455                    pick: |settings_content| {
6456                        settings_content.git.as_ref()?.gutter_debounce.as_ref()
6457                    },
6458                    write: |settings_content, value| {
6459                        settings_content.git.get_or_insert_default().gutter_debounce = value;
6460                    },
6461                }),
6462                metadata: None,
6463                files: USER,
6464            }),
6465        ]
6466    }
6467
6468    fn inline_git_blame_section() -> [SettingsPageItem; 6] {
6469        [
6470            SettingsPageItem::SectionHeader("Inline Git Blame"),
6471            SettingsPageItem::SettingItem(SettingItem {
6472                title: "Enabled",
6473                description: "Whether or not to show Git blame data inline in the currently focused line.",
6474                field: Box::new(SettingField {
6475                    json_path: Some("git.inline_blame.enabled"),
6476                    pick: |settings_content| {
6477                        settings_content
6478                            .git
6479                            .as_ref()?
6480                            .inline_blame
6481                            .as_ref()?
6482                            .enabled
6483                            .as_ref()
6484                    },
6485                    write: |settings_content, value| {
6486                        settings_content
6487                            .git
6488                            .get_or_insert_default()
6489                            .inline_blame
6490                            .get_or_insert_default()
6491                            .enabled = value;
6492                    },
6493                }),
6494                metadata: None,
6495                files: USER,
6496            }),
6497            SettingsPageItem::SettingItem(SettingItem {
6498                title: "Delay",
6499                description: "The delay after which the inline blame information is shown.",
6500                field: Box::new(SettingField {
6501                    json_path: Some("git.inline_blame.delay_ms"),
6502                    pick: |settings_content| {
6503                        settings_content
6504                            .git
6505                            .as_ref()?
6506                            .inline_blame
6507                            .as_ref()?
6508                            .delay_ms
6509                            .as_ref()
6510                    },
6511                    write: |settings_content, value| {
6512                        settings_content
6513                            .git
6514                            .get_or_insert_default()
6515                            .inline_blame
6516                            .get_or_insert_default()
6517                            .delay_ms = value;
6518                    },
6519                }),
6520                metadata: None,
6521                files: USER,
6522            }),
6523            SettingsPageItem::SettingItem(SettingItem {
6524                title: "Padding",
6525                description: "Padding between the end of the source line and the start of the inline blame in columns.",
6526                field: Box::new(SettingField {
6527                    json_path: Some("git.inline_blame.padding"),
6528                    pick: |settings_content| {
6529                        settings_content
6530                            .git
6531                            .as_ref()?
6532                            .inline_blame
6533                            .as_ref()?
6534                            .padding
6535                            .as_ref()
6536                    },
6537                    write: |settings_content, value| {
6538                        settings_content
6539                            .git
6540                            .get_or_insert_default()
6541                            .inline_blame
6542                            .get_or_insert_default()
6543                            .padding = value;
6544                    },
6545                }),
6546                metadata: None,
6547                files: USER,
6548            }),
6549            SettingsPageItem::SettingItem(SettingItem {
6550                title: "Minimum Column",
6551                description: "The minimum column number at which to show the inline blame information.",
6552                field: Box::new(SettingField {
6553                    json_path: Some("git.inline_blame.min_column"),
6554                    pick: |settings_content| {
6555                        settings_content
6556                            .git
6557                            .as_ref()?
6558                            .inline_blame
6559                            .as_ref()?
6560                            .min_column
6561                            .as_ref()
6562                    },
6563                    write: |settings_content, value| {
6564                        settings_content
6565                            .git
6566                            .get_or_insert_default()
6567                            .inline_blame
6568                            .get_or_insert_default()
6569                            .min_column = value;
6570                    },
6571                }),
6572                metadata: None,
6573                files: USER,
6574            }),
6575            SettingsPageItem::SettingItem(SettingItem {
6576                title: "Show Commit Summary",
6577                description: "Show commit summary as part of the inline blame.",
6578                field: Box::new(SettingField {
6579                    json_path: Some("git.inline_blame.show_commit_summary"),
6580                    pick: |settings_content| {
6581                        settings_content
6582                            .git
6583                            .as_ref()?
6584                            .inline_blame
6585                            .as_ref()?
6586                            .show_commit_summary
6587                            .as_ref()
6588                    },
6589                    write: |settings_content, value| {
6590                        settings_content
6591                            .git
6592                            .get_or_insert_default()
6593                            .inline_blame
6594                            .get_or_insert_default()
6595                            .show_commit_summary = value;
6596                    },
6597                }),
6598                metadata: None,
6599                files: USER,
6600            }),
6601        ]
6602    }
6603
6604    fn git_blame_view_section() -> [SettingsPageItem; 2] {
6605        [
6606            SettingsPageItem::SectionHeader("Git Blame View"),
6607            SettingsPageItem::SettingItem(SettingItem {
6608                title: "Show Avatar",
6609                description: "Show the avatar of the author of the commit.",
6610                field: Box::new(SettingField {
6611                    json_path: Some("git.blame.show_avatar"),
6612                    pick: |settings_content| {
6613                        settings_content
6614                            .git
6615                            .as_ref()?
6616                            .blame
6617                            .as_ref()?
6618                            .show_avatar
6619                            .as_ref()
6620                    },
6621                    write: |settings_content, value| {
6622                        settings_content
6623                            .git
6624                            .get_or_insert_default()
6625                            .blame
6626                            .get_or_insert_default()
6627                            .show_avatar = value;
6628                    },
6629                }),
6630                metadata: None,
6631                files: USER,
6632            }),
6633        ]
6634    }
6635
6636    fn branch_picker_section() -> [SettingsPageItem; 2] {
6637        [
6638            SettingsPageItem::SectionHeader("Branch Picker"),
6639            SettingsPageItem::SettingItem(SettingItem {
6640                title: "Show Author Name",
6641                description: "Show author name as part of the commit information in branch picker.",
6642                field: Box::new(SettingField {
6643                    json_path: Some("git.branch_picker.show_author_name"),
6644                    pick: |settings_content| {
6645                        settings_content
6646                            .git
6647                            .as_ref()?
6648                            .branch_picker
6649                            .as_ref()?
6650                            .show_author_name
6651                            .as_ref()
6652                    },
6653                    write: |settings_content, value| {
6654                        settings_content
6655                            .git
6656                            .get_or_insert_default()
6657                            .branch_picker
6658                            .get_or_insert_default()
6659                            .show_author_name = value;
6660                    },
6661                }),
6662                metadata: None,
6663                files: USER,
6664            }),
6665        ]
6666    }
6667
6668    fn git_hunks_section() -> [SettingsPageItem; 3] {
6669        [
6670            SettingsPageItem::SectionHeader("Git Hunks"),
6671            SettingsPageItem::SettingItem(SettingItem {
6672                title: "Hunk Style",
6673                description: "How Git hunks are displayed visually in the editor.",
6674                field: Box::new(SettingField {
6675                    json_path: Some("git.hunk_style"),
6676                    pick: |settings_content| settings_content.git.as_ref()?.hunk_style.as_ref(),
6677                    write: |settings_content, value| {
6678                        settings_content.git.get_or_insert_default().hunk_style = value;
6679                    },
6680                }),
6681                metadata: None,
6682                files: USER,
6683            }),
6684            SettingsPageItem::SettingItem(SettingItem {
6685                title: "Path Style",
6686                description: "Should the name or path be displayed first in the git view.",
6687                field: Box::new(SettingField {
6688                    json_path: Some("git.path_style"),
6689                    pick: |settings_content| settings_content.git.as_ref()?.path_style.as_ref(),
6690                    write: |settings_content, value| {
6691                        settings_content.git.get_or_insert_default().path_style = value;
6692                    },
6693                }),
6694                metadata: None,
6695                files: USER,
6696            }),
6697        ]
6698    }
6699
6700    SettingsPage {
6701        title: "Version Control",
6702        items: concat_sections![
6703            git_integration_section(),
6704            git_gutter_section(),
6705            inline_git_blame_section(),
6706            git_blame_view_section(),
6707            branch_picker_section(),
6708            git_hunks_section(),
6709        ],
6710    }
6711}
6712
6713fn collaboration_page() -> SettingsPage {
6714    fn calls_section() -> [SettingsPageItem; 3] {
6715        [
6716            SettingsPageItem::SectionHeader("Calls"),
6717            SettingsPageItem::SettingItem(SettingItem {
6718                title: "Mute On Join",
6719                description: "Whether the microphone should be muted when joining a channel or a call.",
6720                field: Box::new(SettingField {
6721                    json_path: Some("calls.mute_on_join"),
6722                    pick: |settings_content| settings_content.calls.as_ref()?.mute_on_join.as_ref(),
6723                    write: |settings_content, value| {
6724                        settings_content.calls.get_or_insert_default().mute_on_join = value;
6725                    },
6726                }),
6727                metadata: None,
6728                files: USER,
6729            }),
6730            SettingsPageItem::SettingItem(SettingItem {
6731                title: "Share On Join",
6732                description: "Whether your current project should be shared when joining an empty channel.",
6733                field: Box::new(SettingField {
6734                    json_path: Some("calls.share_on_join"),
6735                    pick: |settings_content| {
6736                        settings_content.calls.as_ref()?.share_on_join.as_ref()
6737                    },
6738                    write: |settings_content, value| {
6739                        settings_content.calls.get_or_insert_default().share_on_join = value;
6740                    },
6741                }),
6742                metadata: None,
6743                files: USER,
6744            }),
6745        ]
6746    }
6747
6748    fn experimental_section() -> [SettingsPageItem; 6] {
6749        [
6750            SettingsPageItem::SectionHeader("Experimental"),
6751            SettingsPageItem::SettingItem(SettingItem {
6752                title: "Rodio Audio",
6753                description: "Opt into the new audio system.",
6754                field: Box::new(SettingField {
6755                    json_path: Some("audio.experimental.rodio_audio"),
6756                    pick: |settings_content| settings_content.audio.as_ref()?.rodio_audio.as_ref(),
6757                    write: |settings_content, value| {
6758                        settings_content.audio.get_or_insert_default().rodio_audio = value;
6759                    },
6760                }),
6761                metadata: None,
6762                files: USER,
6763            }),
6764            SettingsPageItem::SettingItem(SettingItem {
6765                title: "Auto Microphone Volume",
6766                description: "Automatically adjust microphone volume (requires rodio audio).",
6767                field: Box::new(SettingField {
6768                    json_path: Some("audio.experimental.auto_microphone_volume"),
6769                    pick: |settings_content| {
6770                        settings_content
6771                            .audio
6772                            .as_ref()?
6773                            .auto_microphone_volume
6774                            .as_ref()
6775                    },
6776                    write: |settings_content, value| {
6777                        settings_content
6778                            .audio
6779                            .get_or_insert_default()
6780                            .auto_microphone_volume = value;
6781                    },
6782                }),
6783                metadata: None,
6784                files: USER,
6785            }),
6786            SettingsPageItem::SettingItem(SettingItem {
6787                title: "Auto Speaker Volume",
6788                description: "Automatically adjust volume of other call members (requires rodio audio).",
6789                field: Box::new(SettingField {
6790                    json_path: Some("audio.experimental.auto_speaker_volume"),
6791                    pick: |settings_content| {
6792                        settings_content
6793                            .audio
6794                            .as_ref()?
6795                            .auto_speaker_volume
6796                            .as_ref()
6797                    },
6798                    write: |settings_content, value| {
6799                        settings_content
6800                            .audio
6801                            .get_or_insert_default()
6802                            .auto_speaker_volume = value;
6803                    },
6804                }),
6805                metadata: None,
6806                files: USER,
6807            }),
6808            SettingsPageItem::SettingItem(SettingItem {
6809                title: "Denoise",
6810                description: "Remove background noises (requires rodio audio).",
6811                field: Box::new(SettingField {
6812                    json_path: Some("audio.experimental.denoise"),
6813                    pick: |settings_content| settings_content.audio.as_ref()?.denoise.as_ref(),
6814                    write: |settings_content, value| {
6815                        settings_content.audio.get_or_insert_default().denoise = value;
6816                    },
6817                }),
6818                metadata: None,
6819                files: USER,
6820            }),
6821            SettingsPageItem::SettingItem(SettingItem {
6822                title: "Legacy Audio Compatible",
6823                description: "Use audio parameters compatible with previous versions (requires rodio audio).",
6824                field: Box::new(SettingField {
6825                    json_path: Some("audio.experimental.legacy_audio_compatible"),
6826                    pick: |settings_content| {
6827                        settings_content
6828                            .audio
6829                            .as_ref()?
6830                            .legacy_audio_compatible
6831                            .as_ref()
6832                    },
6833                    write: |settings_content, value| {
6834                        settings_content
6835                            .audio
6836                            .get_or_insert_default()
6837                            .legacy_audio_compatible = value;
6838                    },
6839                }),
6840                metadata: None,
6841                files: USER,
6842            }),
6843        ]
6844    }
6845
6846    SettingsPage {
6847        title: "Collaboration",
6848        items: concat_sections![calls_section(), experimental_section()],
6849    }
6850}
6851
6852fn ai_page() -> SettingsPage {
6853    fn general_section() -> [SettingsPageItem; 2] {
6854        [
6855            SettingsPageItem::SectionHeader("General"),
6856            SettingsPageItem::SettingItem(SettingItem {
6857                title: "Disable AI",
6858                description: "Whether to disable all AI features in Zed.",
6859                field: Box::new(SettingField {
6860                    json_path: Some("disable_ai"),
6861                    pick: |settings_content| settings_content.disable_ai.as_ref(),
6862                    write: |settings_content, value| {
6863                        settings_content.disable_ai = value;
6864                    },
6865                }),
6866                metadata: None,
6867                files: USER,
6868            }),
6869        ]
6870    }
6871
6872    fn agent_configuration_section() -> [SettingsPageItem; 12] {
6873        [
6874            SettingsPageItem::SectionHeader("Agent Configuration"),
6875            SettingsPageItem::SettingItem(SettingItem {
6876                title: "Always Allow Tool Actions",
6877                description: "When enabled, the agent can run potentially destructive actions without asking for your confirmation. This setting has no effect on external agents.",
6878                field: Box::new(SettingField {
6879                    json_path: Some("agent.always_allow_tool_actions"),
6880                    pick: |settings_content| {
6881                        settings_content
6882                            .agent
6883                            .as_ref()?
6884                            .always_allow_tool_actions
6885                            .as_ref()
6886                    },
6887                    write: |settings_content, value| {
6888                        settings_content
6889                            .agent
6890                            .get_or_insert_default()
6891                            .always_allow_tool_actions = value;
6892                    },
6893                }),
6894                metadata: None,
6895                files: USER,
6896            }),
6897            SettingsPageItem::SettingItem(SettingItem {
6898                title: "Single File Review",
6899                description: "When enabled, agent edits will also be displayed in single-file buffers for review.",
6900                field: Box::new(SettingField {
6901                    json_path: Some("agent.single_file_review"),
6902                    pick: |settings_content| {
6903                        settings_content.agent.as_ref()?.single_file_review.as_ref()
6904                    },
6905                    write: |settings_content, value| {
6906                        settings_content
6907                            .agent
6908                            .get_or_insert_default()
6909                            .single_file_review = value;
6910                    },
6911                }),
6912                metadata: None,
6913                files: USER,
6914            }),
6915            SettingsPageItem::SettingItem(SettingItem {
6916                title: "Enable Feedback",
6917                description: "Show voting thumbs up/down icon buttons for feedback on agent edits.",
6918                field: Box::new(SettingField {
6919                    json_path: Some("agent.enable_feedback"),
6920                    pick: |settings_content| {
6921                        settings_content.agent.as_ref()?.enable_feedback.as_ref()
6922                    },
6923                    write: |settings_content, value| {
6924                        settings_content
6925                            .agent
6926                            .get_or_insert_default()
6927                            .enable_feedback = value;
6928                    },
6929                }),
6930                metadata: None,
6931                files: USER,
6932            }),
6933            SettingsPageItem::SettingItem(SettingItem {
6934                title: "Notify When Agent Waiting",
6935                description: "Where to show notifications when the agent has completed its response or needs confirmation before running a tool action.",
6936                field: Box::new(SettingField {
6937                    json_path: Some("agent.notify_when_agent_waiting"),
6938                    pick: |settings_content| {
6939                        settings_content
6940                            .agent
6941                            .as_ref()?
6942                            .notify_when_agent_waiting
6943                            .as_ref()
6944                    },
6945                    write: |settings_content, value| {
6946                        settings_content
6947                            .agent
6948                            .get_or_insert_default()
6949                            .notify_when_agent_waiting = value;
6950                    },
6951                }),
6952                metadata: None,
6953                files: USER,
6954            }),
6955            SettingsPageItem::SettingItem(SettingItem {
6956                title: "Play Sound When Agent Done",
6957                description: "Whether to play a sound when the agent has either completed its response, or needs user input.",
6958                field: Box::new(SettingField {
6959                    json_path: Some("agent.play_sound_when_agent_done"),
6960                    pick: |settings_content| {
6961                        settings_content
6962                            .agent
6963                            .as_ref()?
6964                            .play_sound_when_agent_done
6965                            .as_ref()
6966                    },
6967                    write: |settings_content, value| {
6968                        settings_content
6969                            .agent
6970                            .get_or_insert_default()
6971                            .play_sound_when_agent_done = value;
6972                    },
6973                }),
6974                metadata: None,
6975                files: USER,
6976            }),
6977            SettingsPageItem::SettingItem(SettingItem {
6978                title: "Expand Edit Card",
6979                description: "Whether to have edit cards in the agent panel expanded, showing a Preview of the diff.",
6980                field: Box::new(SettingField {
6981                    json_path: Some("agent.expand_edit_card"),
6982                    pick: |settings_content| {
6983                        settings_content.agent.as_ref()?.expand_edit_card.as_ref()
6984                    },
6985                    write: |settings_content, value| {
6986                        settings_content
6987                            .agent
6988                            .get_or_insert_default()
6989                            .expand_edit_card = value;
6990                    },
6991                }),
6992                metadata: None,
6993                files: USER,
6994            }),
6995            SettingsPageItem::SettingItem(SettingItem {
6996                title: "Expand Terminal Card",
6997                description: "Whether to have terminal cards in the agent panel expanded, showing the whole command output.",
6998                field: Box::new(SettingField {
6999                    json_path: Some("agent.expand_terminal_card"),
7000                    pick: |settings_content| {
7001                        settings_content
7002                            .agent
7003                            .as_ref()?
7004                            .expand_terminal_card
7005                            .as_ref()
7006                    },
7007                    write: |settings_content, value| {
7008                        settings_content
7009                            .agent
7010                            .get_or_insert_default()
7011                            .expand_terminal_card = value;
7012                    },
7013                }),
7014                metadata: None,
7015                files: USER,
7016            }),
7017            SettingsPageItem::SettingItem(SettingItem {
7018                title: "Cancel Generation On Terminal Stop",
7019                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.",
7020                field: Box::new(SettingField {
7021                    json_path: Some("agent.cancel_generation_on_terminal_stop"),
7022                    pick: |settings_content| {
7023                        settings_content
7024                            .agent
7025                            .as_ref()?
7026                            .cancel_generation_on_terminal_stop
7027                            .as_ref()
7028                    },
7029                    write: |settings_content, value| {
7030                        settings_content
7031                            .agent
7032                            .get_or_insert_default()
7033                            .cancel_generation_on_terminal_stop = value;
7034                    },
7035                }),
7036                metadata: None,
7037                files: USER,
7038            }),
7039            SettingsPageItem::SettingItem(SettingItem {
7040                title: "Use Modifier To Send",
7041                description: "Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages.",
7042                field: Box::new(SettingField {
7043                    json_path: Some("agent.use_modifier_to_send"),
7044                    pick: |settings_content| {
7045                        settings_content
7046                            .agent
7047                            .as_ref()?
7048                            .use_modifier_to_send
7049                            .as_ref()
7050                    },
7051                    write: |settings_content, value| {
7052                        settings_content
7053                            .agent
7054                            .get_or_insert_default()
7055                            .use_modifier_to_send = value;
7056                    },
7057                }),
7058                metadata: None,
7059                files: USER,
7060            }),
7061            SettingsPageItem::SettingItem(SettingItem {
7062                title: "Message Editor Min Lines",
7063                description: "Minimum number of lines to display in the agent message editor.",
7064                field: Box::new(SettingField {
7065                    json_path: Some("agent.message_editor_min_lines"),
7066                    pick: |settings_content| {
7067                        settings_content
7068                            .agent
7069                            .as_ref()?
7070                            .message_editor_min_lines
7071                            .as_ref()
7072                    },
7073                    write: |settings_content, value| {
7074                        settings_content
7075                            .agent
7076                            .get_or_insert_default()
7077                            .message_editor_min_lines = value;
7078                    },
7079                }),
7080                metadata: None,
7081                files: USER,
7082            }),
7083            SettingsPageItem::SettingItem(SettingItem {
7084                title: "Show Turn Stats",
7085                description: "Whether to show turn statistics like elapsed time during generation and final turn duration.",
7086                field: Box::new(SettingField {
7087                    json_path: Some("agent.show_turn_stats"),
7088                    pick: |settings_content| {
7089                        settings_content.agent.as_ref()?.show_turn_stats.as_ref()
7090                    },
7091                    write: |settings_content, value| {
7092                        settings_content
7093                            .agent
7094                            .get_or_insert_default()
7095                            .show_turn_stats = value;
7096                    },
7097                }),
7098                metadata: None,
7099                files: USER,
7100            }),
7101        ]
7102    }
7103
7104    fn context_servers_section() -> [SettingsPageItem; 2] {
7105        [
7106            SettingsPageItem::SectionHeader("Context Servers"),
7107            SettingsPageItem::SettingItem(SettingItem {
7108                title: "Context Server Timeout",
7109                description: "Default timeout in seconds for context server tool calls. Can be overridden per-server in context_servers configuration.",
7110                field: Box::new(SettingField {
7111                    json_path: Some("context_server_timeout"),
7112                    pick: |settings_content| {
7113                        settings_content.project.context_server_timeout.as_ref()
7114                    },
7115                    write: |settings_content, value| {
7116                        settings_content.project.context_server_timeout = value;
7117                    },
7118                }),
7119                metadata: None,
7120                files: USER | PROJECT,
7121            }),
7122        ]
7123    }
7124
7125    fn edit_prediction_display_sub_section() -> [SettingsPageItem; 2] {
7126        [
7127            SettingsPageItem::SettingItem(SettingItem {
7128                title: "Display Mode",
7129                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.",
7130                field: Box::new(SettingField {
7131                    json_path: Some("edit_prediction.display_mode"),
7132                    pick: |settings_content| {
7133                        settings_content
7134                            .project
7135                            .all_languages
7136                            .edit_predictions
7137                            .as_ref()?
7138                            .mode
7139                            .as_ref()
7140                    },
7141                    write: |settings_content, value| {
7142                        settings_content
7143                            .project
7144                            .all_languages
7145                            .edit_predictions
7146                            .get_or_insert_default()
7147                            .mode = value;
7148                    },
7149                }),
7150                metadata: None,
7151                files: USER,
7152            }),
7153            SettingsPageItem::SettingItem(SettingItem {
7154                title: "Display In Text Threads",
7155                description: "Whether edit predictions are enabled when editing text threads in the agent panel.",
7156                field: Box::new(SettingField {
7157                    json_path: Some("edit_prediction.in_text_threads"),
7158                    pick: |settings_content| {
7159                        settings_content
7160                            .project
7161                            .all_languages
7162                            .edit_predictions
7163                            .as_ref()?
7164                            .enabled_in_text_threads
7165                            .as_ref()
7166                    },
7167                    write: |settings_content, value| {
7168                        settings_content
7169                            .project
7170                            .all_languages
7171                            .edit_predictions
7172                            .get_or_insert_default()
7173                            .enabled_in_text_threads = value;
7174                    },
7175                }),
7176                metadata: None,
7177                files: USER,
7178            }),
7179        ]
7180    }
7181
7182    SettingsPage {
7183        title: "AI",
7184        items: concat_sections![
7185            general_section(),
7186            agent_configuration_section(),
7187            context_servers_section(),
7188            edit_prediction_language_settings_section(),
7189            edit_prediction_display_sub_section()
7190        ],
7191    }
7192}
7193
7194fn network_page() -> SettingsPage {
7195    fn network_section() -> [SettingsPageItem; 3] {
7196        [
7197            SettingsPageItem::SectionHeader("Network"),
7198            SettingsPageItem::SettingItem(SettingItem {
7199                title: "Proxy",
7200                description: "The proxy to use for network requests.",
7201                field: Box::new(SettingField {
7202                    json_path: Some("proxy"),
7203                    pick: |settings_content| settings_content.proxy.as_ref(),
7204                    write: |settings_content, value| {
7205                        settings_content.proxy = value;
7206                    },
7207                }),
7208                metadata: Some(Box::new(SettingsFieldMetadata {
7209                    placeholder: Some("socks5h://localhost:10808"),
7210                    ..Default::default()
7211                })),
7212                files: USER,
7213            }),
7214            SettingsPageItem::SettingItem(SettingItem {
7215                title: "Server URL",
7216                description: "The URL of the Zed server to connect to.",
7217                field: Box::new(SettingField {
7218                    json_path: Some("server_url"),
7219                    pick: |settings_content| settings_content.server_url.as_ref(),
7220                    write: |settings_content, value| {
7221                        settings_content.server_url = value;
7222                    },
7223                }),
7224                metadata: Some(Box::new(SettingsFieldMetadata {
7225                    placeholder: Some("https://zed.dev"),
7226                    ..Default::default()
7227                })),
7228                files: USER,
7229            }),
7230        ]
7231    }
7232
7233    SettingsPage {
7234        title: "Network",
7235        items: concat_sections![network_section()],
7236    }
7237}
7238
7239fn language_settings_field<T>(
7240    settings_content: &SettingsContent,
7241    get_language_setting_field: fn(&LanguageSettingsContent) -> Option<&T>,
7242) -> Option<&T> {
7243    let all_languages = &settings_content.project.all_languages;
7244
7245    active_language()
7246        .and_then(|current_language_name| {
7247            all_languages
7248                .languages
7249                .0
7250                .get(current_language_name.as_ref())
7251        })
7252        .and_then(get_language_setting_field)
7253        .or_else(|| get_language_setting_field(&all_languages.defaults))
7254}
7255
7256fn language_settings_field_mut<T>(
7257    settings_content: &mut SettingsContent,
7258    value: Option<T>,
7259    write: fn(&mut LanguageSettingsContent, Option<T>),
7260) {
7261    let all_languages = &mut settings_content.project.all_languages;
7262    let language_content = if let Some(current_language) = active_language() {
7263        all_languages
7264            .languages
7265            .0
7266            .entry(current_language.to_string())
7267            .or_default()
7268    } else {
7269        &mut all_languages.defaults
7270    };
7271    write(language_content, value);
7272}
7273
7274fn language_settings_data() -> Box<[SettingsPageItem]> {
7275    fn indentation_section() -> [SettingsPageItem; 5] {
7276        [
7277            SettingsPageItem::SectionHeader("Indentation"),
7278            SettingsPageItem::SettingItem(SettingItem {
7279                title: "Tab Size",
7280                description: "How many columns a tab should occupy.",
7281                field: Box::new(SettingField {
7282                    json_path: Some("languages.$(language).tab_size"), // TODO(cameron): not JQ syntax because not URL-safe
7283                    pick: |settings_content| {
7284                        language_settings_field(settings_content, |language| {
7285                            language.tab_size.as_ref()
7286                        })
7287                    },
7288                    write: |settings_content, value| {
7289                        language_settings_field_mut(settings_content, value, |language, value| {
7290                            language.tab_size = value;
7291                        })
7292                    },
7293                }),
7294                metadata: None,
7295                files: USER | PROJECT,
7296            }),
7297            SettingsPageItem::SettingItem(SettingItem {
7298                title: "Hard Tabs",
7299                description: "Whether to indent lines using tab characters, as opposed to multiple spaces.",
7300                field: Box::new(SettingField {
7301                    json_path: Some("languages.$(language).hard_tabs"),
7302                    pick: |settings_content| {
7303                        language_settings_field(settings_content, |language| {
7304                            language.hard_tabs.as_ref()
7305                        })
7306                    },
7307                    write: |settings_content, value| {
7308                        language_settings_field_mut(settings_content, value, |language, value| {
7309                            language.hard_tabs = value;
7310                        })
7311                    },
7312                }),
7313                metadata: None,
7314                files: USER | PROJECT,
7315            }),
7316            SettingsPageItem::SettingItem(SettingItem {
7317                title: "Auto Indent",
7318                description: "Whether indentation should be adjusted based on the context whilst typing.",
7319                field: Box::new(SettingField {
7320                    json_path: Some("languages.$(language).auto_indent"),
7321                    pick: |settings_content| {
7322                        language_settings_field(settings_content, |language| {
7323                            language.auto_indent.as_ref()
7324                        })
7325                    },
7326                    write: |settings_content, value| {
7327                        language_settings_field_mut(settings_content, value, |language, value| {
7328                            language.auto_indent = value;
7329                        })
7330                    },
7331                }),
7332                metadata: None,
7333                files: USER | PROJECT,
7334            }),
7335            SettingsPageItem::SettingItem(SettingItem {
7336                title: "Auto Indent On Paste",
7337                description: "Whether indentation of pasted content should be adjusted based on the context.",
7338                field: Box::new(SettingField {
7339                    json_path: Some("languages.$(language).auto_indent_on_paste"),
7340                    pick: |settings_content| {
7341                        language_settings_field(settings_content, |language| {
7342                            language.auto_indent_on_paste.as_ref()
7343                        })
7344                    },
7345                    write: |settings_content, value| {
7346                        language_settings_field_mut(settings_content, value, |language, value| {
7347                            language.auto_indent_on_paste = value;
7348                        })
7349                    },
7350                }),
7351                metadata: None,
7352                files: USER | PROJECT,
7353            }),
7354        ]
7355    }
7356
7357    fn wrapping_section() -> [SettingsPageItem; 6] {
7358        [
7359            SettingsPageItem::SectionHeader("Wrapping"),
7360            SettingsPageItem::SettingItem(SettingItem {
7361                title: "Soft Wrap",
7362                description: "How to soft-wrap long lines of text.",
7363                field: Box::new(SettingField {
7364                    json_path: Some("languages.$(language).soft_wrap"),
7365                    pick: |settings_content| {
7366                        language_settings_field(settings_content, |language| {
7367                            language.soft_wrap.as_ref()
7368                        })
7369                    },
7370                    write: |settings_content, value| {
7371                        language_settings_field_mut(settings_content, value, |language, value| {
7372                            language.soft_wrap = value;
7373                        })
7374                    },
7375                }),
7376                metadata: None,
7377                files: USER | PROJECT,
7378            }),
7379            SettingsPageItem::SettingItem(SettingItem {
7380                title: "Show Wrap Guides",
7381                description: "Show wrap guides in the editor.",
7382                field: Box::new(SettingField {
7383                    json_path: Some("languages.$(language).show_wrap_guides"),
7384                    pick: |settings_content| {
7385                        language_settings_field(settings_content, |language| {
7386                            language.show_wrap_guides.as_ref()
7387                        })
7388                    },
7389                    write: |settings_content, value| {
7390                        language_settings_field_mut(settings_content, value, |language, value| {
7391                            language.show_wrap_guides = value;
7392                        })
7393                    },
7394                }),
7395                metadata: None,
7396                files: USER | PROJECT,
7397            }),
7398            SettingsPageItem::SettingItem(SettingItem {
7399                title: "Preferred Line Length",
7400                description: "The column at which to soft-wrap lines, for buffers where soft-wrap is enabled.",
7401                field: Box::new(SettingField {
7402                    json_path: Some("languages.$(language).preferred_line_length"),
7403                    pick: |settings_content| {
7404                        language_settings_field(settings_content, |language| {
7405                            language.preferred_line_length.as_ref()
7406                        })
7407                    },
7408                    write: |settings_content, value| {
7409                        language_settings_field_mut(settings_content, value, |language, value| {
7410                            language.preferred_line_length = value;
7411                        })
7412                    },
7413                }),
7414                metadata: None,
7415                files: USER | PROJECT,
7416            }),
7417            SettingsPageItem::SettingItem(SettingItem {
7418                title: "Wrap Guides",
7419                description: "Character counts at which to show wrap guides in the editor.",
7420                field: Box::new(
7421                    SettingField {
7422                        json_path: Some("languages.$(language).wrap_guides"),
7423                        pick: |settings_content| {
7424                            language_settings_field(settings_content, |language| {
7425                                language.wrap_guides.as_ref()
7426                            })
7427                        },
7428                        write: |settings_content, value| {
7429                            language_settings_field_mut(
7430                                settings_content,
7431                                value,
7432                                |language, value| {
7433                                    language.wrap_guides = value;
7434                                },
7435                            )
7436                        },
7437                    }
7438                    .unimplemented(),
7439                ),
7440                metadata: None,
7441                files: USER | PROJECT,
7442            }),
7443            SettingsPageItem::SettingItem(SettingItem {
7444                title: "Allow Rewrap",
7445                description: "Controls where the `editor::rewrap` action is allowed for this language.",
7446                field: Box::new(SettingField {
7447                    json_path: Some("languages.$(language).allow_rewrap"),
7448                    pick: |settings_content| {
7449                        language_settings_field(settings_content, |language| {
7450                            language.allow_rewrap.as_ref()
7451                        })
7452                    },
7453                    write: |settings_content, value| {
7454                        language_settings_field_mut(settings_content, value, |language, value| {
7455                            language.allow_rewrap = value;
7456                        })
7457                    },
7458                }),
7459                metadata: None,
7460                files: USER | PROJECT,
7461            }),
7462        ]
7463    }
7464
7465    fn indent_guides_section() -> [SettingsPageItem; 6] {
7466        [
7467            SettingsPageItem::SectionHeader("Indent Guides"),
7468            SettingsPageItem::SettingItem(SettingItem {
7469                title: "Enabled",
7470                description: "Display indent guides in the editor.",
7471                field: Box::new(SettingField {
7472                    json_path: Some("languages.$(language).indent_guides.enabled"),
7473                    pick: |settings_content| {
7474                        language_settings_field(settings_content, |language| {
7475                            language
7476                                .indent_guides
7477                                .as_ref()
7478                                .and_then(|indent_guides| indent_guides.enabled.as_ref())
7479                        })
7480                    },
7481                    write: |settings_content, value| {
7482                        language_settings_field_mut(settings_content, value, |language, value| {
7483                            language.indent_guides.get_or_insert_default().enabled = value;
7484                        })
7485                    },
7486                }),
7487                metadata: None,
7488                files: USER | PROJECT,
7489            }),
7490            SettingsPageItem::SettingItem(SettingItem {
7491                title: "Line Width",
7492                description: "The width of the indent guides in pixels, between 1 and 10.",
7493                field: Box::new(SettingField {
7494                    json_path: Some("languages.$(language).indent_guides.line_width"),
7495                    pick: |settings_content| {
7496                        language_settings_field(settings_content, |language| {
7497                            language
7498                                .indent_guides
7499                                .as_ref()
7500                                .and_then(|indent_guides| indent_guides.line_width.as_ref())
7501                        })
7502                    },
7503                    write: |settings_content, value| {
7504                        language_settings_field_mut(settings_content, value, |language, value| {
7505                            language.indent_guides.get_or_insert_default().line_width = value;
7506                        })
7507                    },
7508                }),
7509                metadata: None,
7510                files: USER | PROJECT,
7511            }),
7512            SettingsPageItem::SettingItem(SettingItem {
7513                title: "Active Line Width",
7514                description: "The width of the active indent guide in pixels, between 1 and 10.",
7515                field: Box::new(SettingField {
7516                    json_path: Some("languages.$(language).indent_guides.active_line_width"),
7517                    pick: |settings_content| {
7518                        language_settings_field(settings_content, |language| {
7519                            language
7520                                .indent_guides
7521                                .as_ref()
7522                                .and_then(|indent_guides| indent_guides.active_line_width.as_ref())
7523                        })
7524                    },
7525                    write: |settings_content, value| {
7526                        language_settings_field_mut(settings_content, value, |language, value| {
7527                            language
7528                                .indent_guides
7529                                .get_or_insert_default()
7530                                .active_line_width = value;
7531                        })
7532                    },
7533                }),
7534                metadata: None,
7535                files: USER | PROJECT,
7536            }),
7537            SettingsPageItem::SettingItem(SettingItem {
7538                title: "Coloring",
7539                description: "Determines how indent guides are colored.",
7540                field: Box::new(SettingField {
7541                    json_path: Some("languages.$(language).indent_guides.coloring"),
7542                    pick: |settings_content| {
7543                        language_settings_field(settings_content, |language| {
7544                            language
7545                                .indent_guides
7546                                .as_ref()
7547                                .and_then(|indent_guides| indent_guides.coloring.as_ref())
7548                        })
7549                    },
7550                    write: |settings_content, value| {
7551                        language_settings_field_mut(settings_content, value, |language, value| {
7552                            language.indent_guides.get_or_insert_default().coloring = value;
7553                        })
7554                    },
7555                }),
7556                metadata: None,
7557                files: USER | PROJECT,
7558            }),
7559            SettingsPageItem::SettingItem(SettingItem {
7560                title: "Background Coloring",
7561                description: "Determines how indent guide backgrounds are colored.",
7562                field: Box::new(SettingField {
7563                    json_path: Some("languages.$(language).indent_guides.background_coloring"),
7564                    pick: |settings_content| {
7565                        language_settings_field(settings_content, |language| {
7566                            language.indent_guides.as_ref().and_then(|indent_guides| {
7567                                indent_guides.background_coloring.as_ref()
7568                            })
7569                        })
7570                    },
7571                    write: |settings_content, value| {
7572                        language_settings_field_mut(settings_content, value, |language, value| {
7573                            language
7574                                .indent_guides
7575                                .get_or_insert_default()
7576                                .background_coloring = value;
7577                        })
7578                    },
7579                }),
7580                metadata: None,
7581                files: USER | PROJECT,
7582            }),
7583        ]
7584    }
7585
7586    fn formatting_section() -> [SettingsPageItem; 7] {
7587        [
7588            SettingsPageItem::SectionHeader("Formatting"),
7589            SettingsPageItem::SettingItem(SettingItem {
7590                title: "Format On Save",
7591                description: "Whether or not to perform a buffer format before saving.",
7592                field: Box::new(
7593                    // TODO(settings_ui): this setting should just be a bool
7594                    SettingField {
7595                        json_path: Some("languages.$(language).format_on_save"),
7596                        pick: |settings_content| {
7597                            language_settings_field(settings_content, |language| {
7598                                language.format_on_save.as_ref()
7599                            })
7600                        },
7601                        write: |settings_content, value| {
7602                            language_settings_field_mut(
7603                                settings_content,
7604                                value,
7605                                |language, value| {
7606                                    language.format_on_save = value;
7607                                },
7608                            )
7609                        },
7610                    },
7611                ),
7612                metadata: None,
7613                files: USER | PROJECT,
7614            }),
7615            SettingsPageItem::SettingItem(SettingItem {
7616                title: "Remove Trailing Whitespace On Save",
7617                description: "Whether or not to remove any trailing whitespace from lines of a buffer before saving it.",
7618                field: Box::new(SettingField {
7619                    json_path: Some("languages.$(language).remove_trailing_whitespace_on_save"),
7620                    pick: |settings_content| {
7621                        language_settings_field(settings_content, |language| {
7622                            language.remove_trailing_whitespace_on_save.as_ref()
7623                        })
7624                    },
7625                    write: |settings_content, value| {
7626                        language_settings_field_mut(settings_content, value, |language, value| {
7627                            language.remove_trailing_whitespace_on_save = value;
7628                        })
7629                    },
7630                }),
7631                metadata: None,
7632                files: USER | PROJECT,
7633            }),
7634            SettingsPageItem::SettingItem(SettingItem {
7635                title: "Ensure Final Newline On Save",
7636                description: "Whether or not to ensure there's a single newline at the end of a buffer when saving it.",
7637                field: Box::new(SettingField {
7638                    json_path: Some("languages.$(language).ensure_final_newline_on_save"),
7639                    pick: |settings_content| {
7640                        language_settings_field(settings_content, |language| {
7641                            language.ensure_final_newline_on_save.as_ref()
7642                        })
7643                    },
7644                    write: |settings_content, value| {
7645                        language_settings_field_mut(settings_content, value, |language, value| {
7646                            language.ensure_final_newline_on_save = value;
7647                        })
7648                    },
7649                }),
7650                metadata: None,
7651                files: USER | PROJECT,
7652            }),
7653            SettingsPageItem::SettingItem(SettingItem {
7654                title: "Formatter",
7655                description: "How to perform a buffer format.",
7656                field: Box::new(
7657                    SettingField {
7658                        json_path: Some("languages.$(language).formatter"),
7659                        pick: |settings_content| {
7660                            language_settings_field(settings_content, |language| {
7661                                language.formatter.as_ref()
7662                            })
7663                        },
7664                        write: |settings_content, value| {
7665                            language_settings_field_mut(
7666                                settings_content,
7667                                value,
7668                                |language, value| {
7669                                    language.formatter = value;
7670                                },
7671                            )
7672                        },
7673                    }
7674                    .unimplemented(),
7675                ),
7676                metadata: None,
7677                files: USER | PROJECT,
7678            }),
7679            SettingsPageItem::SettingItem(SettingItem {
7680                title: "Use On Type Format",
7681                description: "Whether to use additional LSP queries to format (and amend) the code after every \"trigger\" symbol input, defined by LSP server capabilities",
7682                field: Box::new(SettingField {
7683                    json_path: Some("languages.$(language).use_on_type_format"),
7684                    pick: |settings_content| {
7685                        language_settings_field(settings_content, |language| {
7686                            language.use_on_type_format.as_ref()
7687                        })
7688                    },
7689                    write: |settings_content, value| {
7690                        language_settings_field_mut(settings_content, value, |language, value| {
7691                            language.use_on_type_format = value;
7692                        })
7693                    },
7694                }),
7695                metadata: None,
7696                files: USER | PROJECT,
7697            }),
7698            SettingsPageItem::SettingItem(SettingItem {
7699                title: "Code Actions On Format",
7700                description: "Additional code actions to run when formatting.",
7701                field: Box::new(
7702                    SettingField {
7703                        json_path: Some("languages.$(language).code_actions_on_format"),
7704                        pick: |settings_content| {
7705                            language_settings_field(settings_content, |language| {
7706                                language.code_actions_on_format.as_ref()
7707                            })
7708                        },
7709                        write: |settings_content, value| {
7710                            language_settings_field_mut(
7711                                settings_content,
7712                                value,
7713                                |language, value| {
7714                                    language.code_actions_on_format = value;
7715                                },
7716                            )
7717                        },
7718                    }
7719                    .unimplemented(),
7720                ),
7721                metadata: None,
7722                files: USER | PROJECT,
7723            }),
7724        ]
7725    }
7726
7727    fn autoclose_section() -> [SettingsPageItem; 5] {
7728        [
7729            SettingsPageItem::SectionHeader("Autoclose"),
7730            SettingsPageItem::SettingItem(SettingItem {
7731                title: "Use Autoclose",
7732                description: "Whether to automatically type closing characters for you. For example, when you type '(', Zed will automatically add a closing ')' at the correct position.",
7733                field: Box::new(SettingField {
7734                    json_path: Some("languages.$(language).use_autoclose"),
7735                    pick: |settings_content| {
7736                        language_settings_field(settings_content, |language| {
7737                            language.use_autoclose.as_ref()
7738                        })
7739                    },
7740                    write: |settings_content, value| {
7741                        language_settings_field_mut(settings_content, value, |language, value| {
7742                            language.use_autoclose = value;
7743                        })
7744                    },
7745                }),
7746                metadata: None,
7747                files: USER | PROJECT,
7748            }),
7749            SettingsPageItem::SettingItem(SettingItem {
7750                title: "Use Auto Surround",
7751                description: "Whether to automatically surround text with characters for you. For example, when you select text and type '(', Zed will automatically surround text with ().",
7752                field: Box::new(SettingField {
7753                    json_path: Some("languages.$(language).use_auto_surround"),
7754                    pick: |settings_content| {
7755                        language_settings_field(settings_content, |language| {
7756                            language.use_auto_surround.as_ref()
7757                        })
7758                    },
7759                    write: |settings_content, value| {
7760                        language_settings_field_mut(settings_content, value, |language, value| {
7761                            language.use_auto_surround = value;
7762                        })
7763                    },
7764                }),
7765                metadata: None,
7766                files: USER | PROJECT,
7767            }),
7768            SettingsPageItem::SettingItem(SettingItem {
7769                title: "Always Treat Brackets As Autoclosed",
7770                description: "Controls whether the closing characters are always skipped over and auto-removed no matter how they were inserted.",
7771                field: Box::new(SettingField {
7772                    json_path: Some("languages.$(language).always_treat_brackets_as_autoclosed"),
7773                    pick: |settings_content| {
7774                        language_settings_field(settings_content, |language| {
7775                            language.always_treat_brackets_as_autoclosed.as_ref()
7776                        })
7777                    },
7778                    write: |settings_content, value| {
7779                        language_settings_field_mut(settings_content, value, |language, value| {
7780                            language.always_treat_brackets_as_autoclosed = value;
7781                        })
7782                    },
7783                }),
7784                metadata: None,
7785                files: USER | PROJECT,
7786            }),
7787            SettingsPageItem::SettingItem(SettingItem {
7788                title: "JSX Tag Auto Close",
7789                description: "Whether to automatically close JSX tags.",
7790                field: Box::new(SettingField {
7791                    json_path: Some("languages.$(language).jsx_tag_auto_close"),
7792                    // TODO(settings_ui): this setting should just be a bool
7793                    pick: |settings_content| {
7794                        language_settings_field(settings_content, |language| {
7795                            language.jsx_tag_auto_close.as_ref()?.enabled.as_ref()
7796                        })
7797                    },
7798                    write: |settings_content, value| {
7799                        language_settings_field_mut(settings_content, value, |language, value| {
7800                            language.jsx_tag_auto_close.get_or_insert_default().enabled = value;
7801                        })
7802                    },
7803                }),
7804                metadata: None,
7805                files: USER | PROJECT,
7806            }),
7807        ]
7808    }
7809
7810    fn whitespace_section() -> [SettingsPageItem; 4] {
7811        [
7812            SettingsPageItem::SectionHeader("Whitespace"),
7813            SettingsPageItem::SettingItem(SettingItem {
7814                title: "Show Whitespaces",
7815                description: "Whether to show tabs and spaces in the editor.",
7816                field: Box::new(SettingField {
7817                    json_path: Some("languages.$(language).show_whitespaces"),
7818                    pick: |settings_content| {
7819                        language_settings_field(settings_content, |language| {
7820                            language.show_whitespaces.as_ref()
7821                        })
7822                    },
7823                    write: |settings_content, value| {
7824                        language_settings_field_mut(settings_content, value, |language, value| {
7825                            language.show_whitespaces = value;
7826                        })
7827                    },
7828                }),
7829                metadata: None,
7830                files: USER | PROJECT,
7831            }),
7832            SettingsPageItem::SettingItem(SettingItem {
7833                title: "Space Whitespace Indicator",
7834                description: "Visible character used to render space characters when show_whitespaces is enabled (default: \"\")",
7835                field: Box::new(
7836                    SettingField {
7837                        json_path: Some("languages.$(language).whitespace_map.space"),
7838                        pick: |settings_content| {
7839                            language_settings_field(settings_content, |language| {
7840                                language.whitespace_map.as_ref()?.space.as_ref()
7841                            })
7842                        },
7843                        write: |settings_content, value| {
7844                            language_settings_field_mut(
7845                                settings_content,
7846                                value,
7847                                |language, value| {
7848                                    language.whitespace_map.get_or_insert_default().space = value;
7849                                },
7850                            )
7851                        },
7852                    }
7853                    .unimplemented(),
7854                ),
7855                metadata: None,
7856                files: USER | PROJECT,
7857            }),
7858            SettingsPageItem::SettingItem(SettingItem {
7859                title: "Tab Whitespace Indicator",
7860                description: "Visible character used to render tab characters when show_whitespaces is enabled (default: \"\")",
7861                field: Box::new(
7862                    SettingField {
7863                        json_path: Some("languages.$(language).whitespace_map.tab"),
7864                        pick: |settings_content| {
7865                            language_settings_field(settings_content, |language| {
7866                                language.whitespace_map.as_ref()?.tab.as_ref()
7867                            })
7868                        },
7869                        write: |settings_content, value| {
7870                            language_settings_field_mut(
7871                                settings_content,
7872                                value,
7873                                |language, value| {
7874                                    language.whitespace_map.get_or_insert_default().tab = value;
7875                                },
7876                            )
7877                        },
7878                    }
7879                    .unimplemented(),
7880                ),
7881                metadata: None,
7882                files: USER | PROJECT,
7883            }),
7884        ]
7885    }
7886
7887    fn completions_section() -> [SettingsPageItem; 7] {
7888        [
7889            SettingsPageItem::SectionHeader("Completions"),
7890            SettingsPageItem::SettingItem(SettingItem {
7891                title: "Show Completions On Input",
7892                description: "Whether to pop the completions menu while typing in an editor without explicitly requesting it.",
7893                field: Box::new(SettingField {
7894                    json_path: Some("languages.$(language).show_completions_on_input"),
7895                    pick: |settings_content| {
7896                        language_settings_field(settings_content, |language| {
7897                            language.show_completions_on_input.as_ref()
7898                        })
7899                    },
7900                    write: |settings_content, value| {
7901                        language_settings_field_mut(settings_content, value, |language, value| {
7902                            language.show_completions_on_input = value;
7903                        })
7904                    },
7905                }),
7906                metadata: None,
7907                files: USER | PROJECT,
7908            }),
7909            SettingsPageItem::SettingItem(SettingItem {
7910                title: "Show Completion Documentation",
7911                description: "Whether to display inline and alongside documentation for items in the completions menu.",
7912                field: Box::new(SettingField {
7913                    json_path: Some("languages.$(language).show_completion_documentation"),
7914                    pick: |settings_content| {
7915                        language_settings_field(settings_content, |language| {
7916                            language.show_completion_documentation.as_ref()
7917                        })
7918                    },
7919                    write: |settings_content, value| {
7920                        language_settings_field_mut(settings_content, value, |language, value| {
7921                            language.show_completion_documentation = value;
7922                        })
7923                    },
7924                }),
7925                metadata: None,
7926                files: USER | PROJECT,
7927            }),
7928            SettingsPageItem::SettingItem(SettingItem {
7929                title: "Words",
7930                description: "Controls how words are completed.",
7931                field: Box::new(SettingField {
7932                    json_path: Some("languages.$(language).completions.words"),
7933                    pick: |settings_content| {
7934                        language_settings_field(settings_content, |language| {
7935                            language.completions.as_ref()?.words.as_ref()
7936                        })
7937                    },
7938                    write: |settings_content, value| {
7939                        language_settings_field_mut(settings_content, value, |language, value| {
7940                            language.completions.get_or_insert_default().words = value;
7941                        })
7942                    },
7943                }),
7944                metadata: None,
7945                files: USER | PROJECT,
7946            }),
7947            SettingsPageItem::SettingItem(SettingItem {
7948                title: "Words Min Length",
7949                description: "How many characters has to be in the completions query to automatically show the words-based completions.",
7950                field: Box::new(SettingField {
7951                    json_path: Some("languages.$(language).completions.words_min_length"),
7952                    pick: |settings_content| {
7953                        language_settings_field(settings_content, |language| {
7954                            language.completions.as_ref()?.words_min_length.as_ref()
7955                        })
7956                    },
7957                    write: |settings_content, value| {
7958                        language_settings_field_mut(settings_content, value, |language, value| {
7959                            language
7960                                .completions
7961                                .get_or_insert_default()
7962                                .words_min_length = value;
7963                        })
7964                    },
7965                }),
7966                metadata: None,
7967                files: USER | PROJECT,
7968            }),
7969            SettingsPageItem::SettingItem(SettingItem {
7970                title: "Completion Menu Scrollbar",
7971                description: "When to show the scrollbar in the completion menu.",
7972                field: Box::new(SettingField {
7973                    json_path: Some("editor.completion_menu_scrollbar"),
7974                    pick: |settings_content| {
7975                        settings_content.editor.completion_menu_scrollbar.as_ref()
7976                    },
7977                    write: |settings_content, value| {
7978                        settings_content.editor.completion_menu_scrollbar = value;
7979                    },
7980                }),
7981                metadata: None,
7982                files: USER,
7983            }),
7984            SettingsPageItem::SettingItem(SettingItem {
7985                title: "Completion Detail Alignment",
7986                description: "Whether to align detail text in code completions context menus left or right.",
7987                field: Box::new(SettingField {
7988                    json_path: Some("editor.completion_detail_alignment"),
7989                    pick: |settings_content| {
7990                        settings_content.editor.completion_detail_alignment.as_ref()
7991                    },
7992                    write: |settings_content, value| {
7993                        settings_content.editor.completion_detail_alignment = value;
7994                    },
7995                }),
7996                metadata: None,
7997                files: USER,
7998            }),
7999        ]
8000    }
8001
8002    fn inlay_hints_section() -> [SettingsPageItem; 10] {
8003        [
8004            SettingsPageItem::SectionHeader("Inlay Hints"),
8005            SettingsPageItem::SettingItem(SettingItem {
8006                title: "Enabled",
8007                description: "Global switch to toggle hints on and off.",
8008                field: Box::new(SettingField {
8009                    json_path: Some("languages.$(language).inlay_hints.enabled"),
8010                    pick: |settings_content| {
8011                        language_settings_field(settings_content, |language| {
8012                            language.inlay_hints.as_ref()?.enabled.as_ref()
8013                        })
8014                    },
8015                    write: |settings_content, value| {
8016                        language_settings_field_mut(settings_content, value, |language, value| {
8017                            language.inlay_hints.get_or_insert_default().enabled = value;
8018                        })
8019                    },
8020                }),
8021                metadata: None,
8022                files: USER | PROJECT,
8023            }),
8024            SettingsPageItem::SettingItem(SettingItem {
8025                title: "Show Value Hints",
8026                description: "Global switch to toggle inline values on and off when debugging.",
8027                field: Box::new(SettingField {
8028                    json_path: Some("languages.$(language).inlay_hints.show_value_hints"),
8029                    pick: |settings_content| {
8030                        language_settings_field(settings_content, |language| {
8031                            language.inlay_hints.as_ref()?.show_value_hints.as_ref()
8032                        })
8033                    },
8034                    write: |settings_content, value| {
8035                        language_settings_field_mut(settings_content, value, |language, value| {
8036                            language
8037                                .inlay_hints
8038                                .get_or_insert_default()
8039                                .show_value_hints = value;
8040                        })
8041                    },
8042                }),
8043                metadata: None,
8044                files: USER | PROJECT,
8045            }),
8046            SettingsPageItem::SettingItem(SettingItem {
8047                title: "Show Type Hints",
8048                description: "Whether type hints should be shown.",
8049                field: Box::new(SettingField {
8050                    json_path: Some("languages.$(language).inlay_hints.show_type_hints"),
8051                    pick: |settings_content| {
8052                        language_settings_field(settings_content, |language| {
8053                            language.inlay_hints.as_ref()?.show_type_hints.as_ref()
8054                        })
8055                    },
8056                    write: |settings_content, value| {
8057                        language_settings_field_mut(settings_content, value, |language, value| {
8058                            language.inlay_hints.get_or_insert_default().show_type_hints = value;
8059                        })
8060                    },
8061                }),
8062                metadata: None,
8063                files: USER | PROJECT,
8064            }),
8065            SettingsPageItem::SettingItem(SettingItem {
8066                title: "Show Parameter Hints",
8067                description: "Whether parameter hints should be shown.",
8068                field: Box::new(SettingField {
8069                    json_path: Some("languages.$(language).inlay_hints.show_parameter_hints"),
8070                    pick: |settings_content| {
8071                        language_settings_field(settings_content, |language| {
8072                            language.inlay_hints.as_ref()?.show_parameter_hints.as_ref()
8073                        })
8074                    },
8075                    write: |settings_content, value| {
8076                        language_settings_field_mut(settings_content, value, |language, value| {
8077                            language
8078                                .inlay_hints
8079                                .get_or_insert_default()
8080                                .show_parameter_hints = value;
8081                        })
8082                    },
8083                }),
8084                metadata: None,
8085                files: USER | PROJECT,
8086            }),
8087            SettingsPageItem::SettingItem(SettingItem {
8088                title: "Show Other Hints",
8089                description: "Whether other hints should be shown.",
8090                field: Box::new(SettingField {
8091                    json_path: Some("languages.$(language).inlay_hints.show_other_hints"),
8092                    pick: |settings_content| {
8093                        language_settings_field(settings_content, |language| {
8094                            language.inlay_hints.as_ref()?.show_other_hints.as_ref()
8095                        })
8096                    },
8097                    write: |settings_content, value| {
8098                        language_settings_field_mut(settings_content, value, |language, value| {
8099                            language
8100                                .inlay_hints
8101                                .get_or_insert_default()
8102                                .show_other_hints = value;
8103                        })
8104                    },
8105                }),
8106                metadata: None,
8107                files: USER | PROJECT,
8108            }),
8109            SettingsPageItem::SettingItem(SettingItem {
8110                title: "Show Background",
8111                description: "Show a background for inlay hints.",
8112                field: Box::new(SettingField {
8113                    json_path: Some("languages.$(language).inlay_hints.show_background"),
8114                    pick: |settings_content| {
8115                        language_settings_field(settings_content, |language| {
8116                            language.inlay_hints.as_ref()?.show_background.as_ref()
8117                        })
8118                    },
8119                    write: |settings_content, value| {
8120                        language_settings_field_mut(settings_content, value, |language, value| {
8121                            language.inlay_hints.get_or_insert_default().show_background = value;
8122                        })
8123                    },
8124                }),
8125                metadata: None,
8126                files: USER | PROJECT,
8127            }),
8128            SettingsPageItem::SettingItem(SettingItem {
8129                title: "Edit Debounce Ms",
8130                description: "Whether or not to debounce inlay hints updates after buffer edits (set to 0 to disable debouncing).",
8131                field: Box::new(SettingField {
8132                    json_path: Some("languages.$(language).inlay_hints.edit_debounce_ms"),
8133                    pick: |settings_content| {
8134                        language_settings_field(settings_content, |language| {
8135                            language.inlay_hints.as_ref()?.edit_debounce_ms.as_ref()
8136                        })
8137                    },
8138                    write: |settings_content, value| {
8139                        language_settings_field_mut(settings_content, value, |language, value| {
8140                            language
8141                                .inlay_hints
8142                                .get_or_insert_default()
8143                                .edit_debounce_ms = value;
8144                        })
8145                    },
8146                }),
8147                metadata: None,
8148                files: USER | PROJECT,
8149            }),
8150            SettingsPageItem::SettingItem(SettingItem {
8151                title: "Scroll Debounce Ms",
8152                description: "Whether or not to debounce inlay hints updates after buffer scrolls (set to 0 to disable debouncing).",
8153                field: Box::new(SettingField {
8154                    json_path: Some("languages.$(language).inlay_hints.scroll_debounce_ms"),
8155                    pick: |settings_content| {
8156                        language_settings_field(settings_content, |language| {
8157                            language.inlay_hints.as_ref()?.scroll_debounce_ms.as_ref()
8158                        })
8159                    },
8160                    write: |settings_content, value| {
8161                        language_settings_field_mut(settings_content, value, |language, value| {
8162                            language
8163                                .inlay_hints
8164                                .get_or_insert_default()
8165                                .scroll_debounce_ms = value;
8166                        })
8167                    },
8168                }),
8169                metadata: None,
8170                files: USER | PROJECT,
8171            }),
8172            SettingsPageItem::SettingItem(SettingItem {
8173                title: "Toggle On Modifiers Press",
8174                description: "Toggles inlay hints (hides or shows) when the user presses the modifiers specified.",
8175                field: Box::new(
8176                    SettingField {
8177                        json_path: Some(
8178                            "languages.$(language).inlay_hints.toggle_on_modifiers_press",
8179                        ),
8180                        pick: |settings_content| {
8181                            language_settings_field(settings_content, |language| {
8182                                language
8183                                    .inlay_hints
8184                                    .as_ref()?
8185                                    .toggle_on_modifiers_press
8186                                    .as_ref()
8187                            })
8188                        },
8189                        write: |settings_content, value| {
8190                            language_settings_field_mut(
8191                                settings_content,
8192                                value,
8193                                |language, value| {
8194                                    language
8195                                        .inlay_hints
8196                                        .get_or_insert_default()
8197                                        .toggle_on_modifiers_press = value;
8198                                },
8199                            )
8200                        },
8201                    }
8202                    .unimplemented(),
8203                ),
8204                metadata: None,
8205                files: USER | PROJECT,
8206            }),
8207        ]
8208    }
8209
8210    fn tasks_section() -> [SettingsPageItem; 4] {
8211        [
8212            SettingsPageItem::SectionHeader("Tasks"),
8213            SettingsPageItem::SettingItem(SettingItem {
8214                title: "Enabled",
8215                description: "Whether tasks are enabled for this language.",
8216                field: Box::new(SettingField {
8217                    json_path: Some("languages.$(language).tasks.enabled"),
8218                    pick: |settings_content| {
8219                        language_settings_field(settings_content, |language| {
8220                            language.tasks.as_ref()?.enabled.as_ref()
8221                        })
8222                    },
8223                    write: |settings_content, value| {
8224                        language_settings_field_mut(settings_content, value, |language, value| {
8225                            language.tasks.get_or_insert_default().enabled = value;
8226                        })
8227                    },
8228                }),
8229                metadata: None,
8230                files: USER | PROJECT,
8231            }),
8232            SettingsPageItem::SettingItem(SettingItem {
8233                title: "Variables",
8234                description: "Extra task variables to set for a particular language.",
8235                field: Box::new(
8236                    SettingField {
8237                        json_path: Some("languages.$(language).tasks.variables"),
8238                        pick: |settings_content| {
8239                            language_settings_field(settings_content, |language| {
8240                                language.tasks.as_ref()?.variables.as_ref()
8241                            })
8242                        },
8243                        write: |settings_content, value| {
8244                            language_settings_field_mut(
8245                                settings_content,
8246                                value,
8247                                |language, value| {
8248                                    language.tasks.get_or_insert_default().variables = value;
8249                                },
8250                            )
8251                        },
8252                    }
8253                    .unimplemented(),
8254                ),
8255                metadata: None,
8256                files: USER | PROJECT,
8257            }),
8258            SettingsPageItem::SettingItem(SettingItem {
8259                title: "Prefer LSP",
8260                description: "Use LSP tasks over Zed language extension tasks.",
8261                field: Box::new(SettingField {
8262                    json_path: Some("languages.$(language).tasks.prefer_lsp"),
8263                    pick: |settings_content| {
8264                        language_settings_field(settings_content, |language| {
8265                            language.tasks.as_ref()?.prefer_lsp.as_ref()
8266                        })
8267                    },
8268                    write: |settings_content, value| {
8269                        language_settings_field_mut(settings_content, value, |language, value| {
8270                            language.tasks.get_or_insert_default().prefer_lsp = value;
8271                        })
8272                    },
8273                }),
8274                metadata: None,
8275                files: USER | PROJECT,
8276            }),
8277        ]
8278    }
8279
8280    fn miscellaneous_section() -> [SettingsPageItem; 6] {
8281        [
8282            SettingsPageItem::SectionHeader("Miscellaneous"),
8283            SettingsPageItem::SettingItem(SettingItem {
8284                title: "Word Diff Enabled",
8285                description: "Whether to enable word diff highlighting in the editor. When enabled, changed words within modified lines are highlighted to show exactly what changed.",
8286                field: Box::new(SettingField {
8287                    json_path: Some("languages.$(language).word_diff_enabled"),
8288                    pick: |settings_content| {
8289                        language_settings_field(settings_content, |language| {
8290                            language.word_diff_enabled.as_ref()
8291                        })
8292                    },
8293                    write: |settings_content, value| {
8294                        language_settings_field_mut(settings_content, value, |language, value| {
8295                            language.word_diff_enabled = value;
8296                        })
8297                    },
8298                }),
8299                metadata: None,
8300                files: USER | PROJECT,
8301            }),
8302            SettingsPageItem::SettingItem(SettingItem {
8303                title: "Debuggers",
8304                description: "Preferred debuggers for this language.",
8305                field: Box::new(
8306                    SettingField {
8307                        json_path: Some("languages.$(language).debuggers"),
8308                        pick: |settings_content| {
8309                            language_settings_field(settings_content, |language| {
8310                                language.debuggers.as_ref()
8311                            })
8312                        },
8313                        write: |settings_content, value| {
8314                            language_settings_field_mut(
8315                                settings_content,
8316                                value,
8317                                |language, value| {
8318                                    language.debuggers = value;
8319                                },
8320                            )
8321                        },
8322                    }
8323                    .unimplemented(),
8324                ),
8325                metadata: None,
8326                files: USER | PROJECT,
8327            }),
8328            SettingsPageItem::SettingItem(SettingItem {
8329                title: "Middle Click Paste",
8330                description: "Enable middle-click paste on Linux.",
8331                field: Box::new(SettingField {
8332                    json_path: Some("languages.$(language).editor.middle_click_paste"),
8333                    pick: |settings_content| settings_content.editor.middle_click_paste.as_ref(),
8334                    write: |settings_content, value| {
8335                        settings_content.editor.middle_click_paste = value;
8336                    },
8337                }),
8338                metadata: None,
8339                files: USER,
8340            }),
8341            SettingsPageItem::SettingItem(SettingItem {
8342                title: "Extend Comment On Newline",
8343                description: "Whether to start a new line with a comment when a previous line is a comment as well.",
8344                field: Box::new(SettingField {
8345                    json_path: Some("languages.$(language).extend_comment_on_newline"),
8346                    pick: |settings_content| {
8347                        language_settings_field(settings_content, |language| {
8348                            language.extend_comment_on_newline.as_ref()
8349                        })
8350                    },
8351                    write: |settings_content, value| {
8352                        language_settings_field_mut(settings_content, value, |language, value| {
8353                            language.extend_comment_on_newline = value;
8354                        })
8355                    },
8356                }),
8357                metadata: None,
8358                files: USER | PROJECT,
8359            }),
8360            SettingsPageItem::SettingItem(SettingItem {
8361                title: "Colorize Brackets",
8362                description: "Whether to colorize brackets in the editor.",
8363                field: Box::new(SettingField {
8364                    json_path: Some("languages.$(language).colorize_brackets"),
8365                    pick: |settings_content| {
8366                        language_settings_field(settings_content, |language| {
8367                            language.colorize_brackets.as_ref()
8368                        })
8369                    },
8370                    write: |settings_content, value| {
8371                        language_settings_field_mut(settings_content, value, |language, value| {
8372                            language.colorize_brackets = value;
8373                        })
8374                    },
8375                }),
8376                metadata: None,
8377                files: USER | PROJECT,
8378            }),
8379        ]
8380    }
8381
8382    fn global_only_miscellaneous_sub_section() -> [SettingsPageItem; 3] {
8383        [
8384            SettingsPageItem::SettingItem(SettingItem {
8385                title: "Image Viewer",
8386                description: "The unit for image file sizes.",
8387                field: Box::new(SettingField {
8388                    json_path: Some("image_viewer.unit"),
8389                    pick: |settings_content| {
8390                        settings_content
8391                            .image_viewer
8392                            .as_ref()
8393                            .and_then(|image_viewer| image_viewer.unit.as_ref())
8394                    },
8395                    write: |settings_content, value| {
8396                        settings_content.image_viewer.get_or_insert_default().unit = value;
8397                    },
8398                }),
8399                metadata: None,
8400                files: USER,
8401            }),
8402            SettingsPageItem::SettingItem(SettingItem {
8403                title: "Auto Replace Emoji Shortcode",
8404                description: "Whether to automatically replace emoji shortcodes with emoji characters.",
8405                field: Box::new(SettingField {
8406                    json_path: Some("message_editor.auto_replace_emoji_shortcode"),
8407                    pick: |settings_content| {
8408                        settings_content
8409                            .message_editor
8410                            .as_ref()
8411                            .and_then(|message_editor| {
8412                                message_editor.auto_replace_emoji_shortcode.as_ref()
8413                            })
8414                    },
8415                    write: |settings_content, value| {
8416                        settings_content
8417                            .message_editor
8418                            .get_or_insert_default()
8419                            .auto_replace_emoji_shortcode = value;
8420                    },
8421                }),
8422                metadata: None,
8423                files: USER,
8424            }),
8425            SettingsPageItem::SettingItem(SettingItem {
8426                title: "Drop Size Target",
8427                description: "Relative size of the drop target in the editor that will open dropped file as a split pane.",
8428                field: Box::new(SettingField {
8429                    json_path: Some("drop_target_size"),
8430                    pick: |settings_content| settings_content.workspace.drop_target_size.as_ref(),
8431                    write: |settings_content, value| {
8432                        settings_content.workspace.drop_target_size = value;
8433                    },
8434                }),
8435                metadata: None,
8436                files: USER,
8437            }),
8438        ]
8439    }
8440
8441    let is_global = active_language().is_none();
8442
8443    let lsp_document_colors_item = [SettingsPageItem::SettingItem(SettingItem {
8444        title: "LSP Document Colors",
8445        description: "How to render LSP color previews in the editor.",
8446        field: Box::new(SettingField {
8447            json_path: Some("lsp_document_colors"),
8448            pick: |settings_content| settings_content.editor.lsp_document_colors.as_ref(),
8449            write: |settings_content, value| {
8450                settings_content.editor.lsp_document_colors = value;
8451            },
8452        }),
8453        metadata: None,
8454        files: USER,
8455    })];
8456
8457    if is_global {
8458        concat_sections!(
8459            indentation_section(),
8460            wrapping_section(),
8461            indent_guides_section(),
8462            formatting_section(),
8463            autoclose_section(),
8464            whitespace_section(),
8465            completions_section(),
8466            inlay_hints_section(),
8467            lsp_document_colors_item,
8468            tasks_section(),
8469            miscellaneous_section(),
8470            global_only_miscellaneous_sub_section(),
8471        )
8472    } else {
8473        concat_sections!(
8474            indentation_section(),
8475            wrapping_section(),
8476            indent_guides_section(),
8477            formatting_section(),
8478            autoclose_section(),
8479            whitespace_section(),
8480            completions_section(),
8481            inlay_hints_section(),
8482            tasks_section(),
8483            miscellaneous_section(),
8484        )
8485    }
8486}
8487
8488/// LanguageSettings items that should be included in the "Languages & Tools" page
8489/// not the "Editor" page
8490fn non_editor_language_settings_data() -> Box<[SettingsPageItem]> {
8491    fn lsp_section() -> [SettingsPageItem; 5] {
8492        [
8493            SettingsPageItem::SectionHeader("LSP"),
8494            SettingsPageItem::SettingItem(SettingItem {
8495                title: "Enable Language Server",
8496                description: "Whether to use language servers to provide code intelligence.",
8497                field: Box::new(SettingField {
8498                    json_path: Some("languages.$(language).enable_language_server"),
8499                    pick: |settings_content| {
8500                        language_settings_field(settings_content, |language| {
8501                            language.enable_language_server.as_ref()
8502                        })
8503                    },
8504                    write: |settings_content, value| {
8505                        language_settings_field_mut(settings_content, value, |language, value| {
8506                            language.enable_language_server = value;
8507                        })
8508                    },
8509                }),
8510                metadata: None,
8511                files: USER | PROJECT,
8512            }),
8513            SettingsPageItem::SettingItem(SettingItem {
8514                title: "Language Servers",
8515                description: "The list of language servers to use (or disable) for this language.",
8516                field: Box::new(
8517                    SettingField {
8518                        json_path: Some("languages.$(language).language_servers"),
8519                        pick: |settings_content| {
8520                            language_settings_field(settings_content, |language| {
8521                                language.language_servers.as_ref()
8522                            })
8523                        },
8524                        write: |settings_content, value| {
8525                            language_settings_field_mut(
8526                                settings_content,
8527                                value,
8528                                |language, value| {
8529                                    language.language_servers = value;
8530                                },
8531                            )
8532                        },
8533                    }
8534                    .unimplemented(),
8535                ),
8536                metadata: None,
8537                files: USER | PROJECT,
8538            }),
8539            SettingsPageItem::SettingItem(SettingItem {
8540                title: "Linked Edits",
8541                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.",
8542                field: Box::new(SettingField {
8543                    json_path: Some("languages.$(language).linked_edits"),
8544                    pick: |settings_content| {
8545                        language_settings_field(settings_content, |language| {
8546                            language.linked_edits.as_ref()
8547                        })
8548                    },
8549                    write: |settings_content, value| {
8550                        language_settings_field_mut(settings_content, value, |language, value| {
8551                            language.linked_edits = value;
8552                        })
8553                    },
8554                }),
8555                metadata: None,
8556                files: USER | PROJECT,
8557            }),
8558            SettingsPageItem::SettingItem(SettingItem {
8559                title: "Go To Definition Fallback",
8560                description: "Whether to follow-up empty Go to definition responses from the language server.",
8561                field: Box::new(SettingField {
8562                    json_path: Some("go_to_definition_fallback"),
8563                    pick: |settings_content| {
8564                        settings_content.editor.go_to_definition_fallback.as_ref()
8565                    },
8566                    write: |settings_content, value| {
8567                        settings_content.editor.go_to_definition_fallback = value;
8568                    },
8569                }),
8570                metadata: None,
8571                files: USER,
8572            }),
8573        ]
8574    }
8575
8576    fn lsp_completions_section() -> [SettingsPageItem; 4] {
8577        [
8578            SettingsPageItem::SectionHeader("LSP Completions"),
8579            SettingsPageItem::SettingItem(SettingItem {
8580                title: "Enabled",
8581                description: "Whether to fetch LSP completions or not.",
8582                field: Box::new(SettingField {
8583                    json_path: Some("languages.$(language).completions.lsp"),
8584                    pick: |settings_content| {
8585                        language_settings_field(settings_content, |language| {
8586                            language.completions.as_ref()?.lsp.as_ref()
8587                        })
8588                    },
8589                    write: |settings_content, value| {
8590                        language_settings_field_mut(settings_content, value, |language, value| {
8591                            language.completions.get_or_insert_default().lsp = value;
8592                        })
8593                    },
8594                }),
8595                metadata: None,
8596                files: USER | PROJECT,
8597            }),
8598            SettingsPageItem::SettingItem(SettingItem {
8599                title: "Fetch Timeout (milliseconds)",
8600                description: "When fetching LSP completions, determines how long to wait for a response of a particular server (set to 0 to wait indefinitely).",
8601                field: Box::new(SettingField {
8602                    json_path: Some("languages.$(language).completions.lsp_fetch_timeout_ms"),
8603                    pick: |settings_content| {
8604                        language_settings_field(settings_content, |language| {
8605                            language.completions.as_ref()?.lsp_fetch_timeout_ms.as_ref()
8606                        })
8607                    },
8608                    write: |settings_content, value| {
8609                        language_settings_field_mut(settings_content, value, |language, value| {
8610                            language
8611                                .completions
8612                                .get_or_insert_default()
8613                                .lsp_fetch_timeout_ms = value;
8614                        })
8615                    },
8616                }),
8617                metadata: None,
8618                files: USER | PROJECT,
8619            }),
8620            SettingsPageItem::SettingItem(SettingItem {
8621                title: "Insert Mode",
8622                description: "Controls how LSP completions are inserted.",
8623                field: Box::new(SettingField {
8624                    json_path: Some("languages.$(language).completions.lsp_insert_mode"),
8625                    pick: |settings_content| {
8626                        language_settings_field(settings_content, |language| {
8627                            language.completions.as_ref()?.lsp_insert_mode.as_ref()
8628                        })
8629                    },
8630                    write: |settings_content, value| {
8631                        language_settings_field_mut(settings_content, value, |language, value| {
8632                            language.completions.get_or_insert_default().lsp_insert_mode = value;
8633                        })
8634                    },
8635                }),
8636                metadata: None,
8637                files: USER | PROJECT,
8638            }),
8639        ]
8640    }
8641
8642    fn debugger_section() -> [SettingsPageItem; 2] {
8643        [
8644            SettingsPageItem::SectionHeader("Debuggers"),
8645            SettingsPageItem::SettingItem(SettingItem {
8646                title: "Debuggers",
8647                description: "Preferred debuggers for this language.",
8648                field: Box::new(
8649                    SettingField {
8650                        json_path: Some("languages.$(language).debuggers"),
8651                        pick: |settings_content| {
8652                            language_settings_field(settings_content, |language| {
8653                                language.debuggers.as_ref()
8654                            })
8655                        },
8656                        write: |settings_content, value| {
8657                            language_settings_field_mut(
8658                                settings_content,
8659                                value,
8660                                |language, value| {
8661                                    language.debuggers = value;
8662                                },
8663                            )
8664                        },
8665                    }
8666                    .unimplemented(),
8667                ),
8668                metadata: None,
8669                files: USER | PROJECT,
8670            }),
8671        ]
8672    }
8673
8674    fn prettier_section() -> [SettingsPageItem; 5] {
8675        [
8676            SettingsPageItem::SectionHeader("Prettier"),
8677            SettingsPageItem::SettingItem(SettingItem {
8678                title: "Allowed",
8679                description: "Enables or disables formatting with Prettier for a given language.",
8680                field: Box::new(SettingField {
8681                    json_path: Some("languages.$(language).prettier.allowed"),
8682                    pick: |settings_content| {
8683                        language_settings_field(settings_content, |language| {
8684                            language.prettier.as_ref()?.allowed.as_ref()
8685                        })
8686                    },
8687                    write: |settings_content, value| {
8688                        language_settings_field_mut(settings_content, value, |language, value| {
8689                            language.prettier.get_or_insert_default().allowed = value;
8690                        })
8691                    },
8692                }),
8693                metadata: None,
8694                files: USER | PROJECT,
8695            }),
8696            SettingsPageItem::SettingItem(SettingItem {
8697                title: "Parser",
8698                description: "Forces Prettier integration to use a specific parser name when formatting files with the language.",
8699                field: Box::new(SettingField {
8700                    json_path: Some("languages.$(language).prettier.parser"),
8701                    pick: |settings_content| {
8702                        language_settings_field(settings_content, |language| {
8703                            language.prettier.as_ref()?.parser.as_ref()
8704                        })
8705                    },
8706                    write: |settings_content, value| {
8707                        language_settings_field_mut(settings_content, value, |language, value| {
8708                            language.prettier.get_or_insert_default().parser = value;
8709                        })
8710                    },
8711                }),
8712                metadata: None,
8713                files: USER | PROJECT,
8714            }),
8715            SettingsPageItem::SettingItem(SettingItem {
8716                title: "Plugins",
8717                description: "Forces Prettier integration to use specific plugins when formatting files with the language.",
8718                field: Box::new(
8719                    SettingField {
8720                        json_path: Some("languages.$(language).prettier.plugins"),
8721                        pick: |settings_content| {
8722                            language_settings_field(settings_content, |language| {
8723                                language.prettier.as_ref()?.plugins.as_ref()
8724                            })
8725                        },
8726                        write: |settings_content, value| {
8727                            language_settings_field_mut(
8728                                settings_content,
8729                                value,
8730                                |language, value| {
8731                                    language.prettier.get_or_insert_default().plugins = value;
8732                                },
8733                            )
8734                        },
8735                    }
8736                    .unimplemented(),
8737                ),
8738                metadata: None,
8739                files: USER | PROJECT,
8740            }),
8741            SettingsPageItem::SettingItem(SettingItem {
8742                title: "Options",
8743                description: "Default Prettier options, in the format as in package.json section for Prettier.",
8744                field: Box::new(
8745                    SettingField {
8746                        json_path: Some("languages.$(language).prettier.options"),
8747                        pick: |settings_content| {
8748                            language_settings_field(settings_content, |language| {
8749                                language.prettier.as_ref()?.options.as_ref()
8750                            })
8751                        },
8752                        write: |settings_content, value| {
8753                            language_settings_field_mut(
8754                                settings_content,
8755                                value,
8756                                |language, value| {
8757                                    language.prettier.get_or_insert_default().options = value;
8758                                },
8759                            )
8760                        },
8761                    }
8762                    .unimplemented(),
8763                ),
8764                metadata: None,
8765                files: USER | PROJECT,
8766            }),
8767        ]
8768    }
8769
8770    concat_sections!(
8771        lsp_section(),
8772        lsp_completions_section(),
8773        debugger_section(),
8774        prettier_section(),
8775    )
8776}
8777
8778fn edit_prediction_language_settings_section() -> [SettingsPageItem; 4] {
8779    [
8780        SettingsPageItem::SectionHeader("Edit Predictions"),
8781        SettingsPageItem::SubPageLink(SubPageLink {
8782            title: "Configure Providers".into(),
8783            r#type: Default::default(),
8784            json_path: Some("edit_predictions.providers"),
8785            description: Some("Set up different edit prediction providers in complement to Zed's built-in Zeta model.".into()),
8786            in_json: false,
8787            files: USER,
8788            render: render_edit_prediction_setup_page
8789        }),
8790        SettingsPageItem::SettingItem(SettingItem {
8791            title: "Show Edit Predictions",
8792            description: "Controls whether edit predictions are shown immediately or manually.",
8793            field: Box::new(SettingField {
8794                json_path: Some("languages.$(language).show_edit_predictions"),
8795                pick: |settings_content| {
8796                    language_settings_field(settings_content, |language| {
8797                        language.show_edit_predictions.as_ref()
8798                    })
8799                },
8800                write: |settings_content, value| {
8801                    language_settings_field_mut(settings_content, value, |language, value| {
8802                        language.show_edit_predictions = value;
8803                    })
8804                },
8805            }),
8806            metadata: None,
8807            files: USER | PROJECT,
8808        }),
8809        SettingsPageItem::SettingItem(SettingItem {
8810            title: "Disable in Language Scopes",
8811            description: "Controls whether edit predictions are shown in the given language scopes.",
8812            field: Box::new(
8813                SettingField {
8814                    json_path: Some("languages.$(language).edit_predictions_disabled_in"),
8815                    pick: |settings_content| {
8816                        language_settings_field(settings_content, |language| {
8817                            language.edit_predictions_disabled_in.as_ref()
8818                        })
8819                    },
8820                    write: |settings_content, value| {
8821                        language_settings_field_mut(settings_content, value, |language, value| {
8822                            language.edit_predictions_disabled_in = value;
8823                        })
8824                    },
8825                }
8826                .unimplemented(),
8827            ),
8828            metadata: None,
8829            files: USER | PROJECT,
8830        }),
8831    ]
8832}
8833
8834fn show_scrollbar_or_editor(
8835    settings_content: &SettingsContent,
8836    show: fn(&SettingsContent) -> Option<&settings::ShowScrollbar>,
8837) -> Option<&settings::ShowScrollbar> {
8838    show(settings_content).or(settings_content
8839        .editor
8840        .scrollbar
8841        .as_ref()
8842        .and_then(|scrollbar| scrollbar.show.as_ref()))
8843}
8844
8845fn dynamic_variants<T>() -> &'static [T::Discriminant]
8846where
8847    T: strum::IntoDiscriminant,
8848    T::Discriminant: strum::VariantArray,
8849{
8850    <<T as strum::IntoDiscriminant>::Discriminant as strum::VariantArray>::VARIANTS
8851}