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; 20] {
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: "Show Scrollbar",
4463                description: "Show the scrollbar in the project panel.",
4464                field: Box::new(SettingField {
4465                    json_path: Some("project_panel.scrollbar.show"),
4466                    pick: |settings_content| {
4467                        show_scrollbar_or_editor(settings_content, |settings_content| {
4468                            settings_content
4469                                .project_panel
4470                                .as_ref()?
4471                                .scrollbar
4472                                .as_ref()?
4473                                .show
4474                                .as_ref()
4475                        })
4476                    },
4477                    write: |settings_content, value| {
4478                        settings_content
4479                            .project_panel
4480                            .get_or_insert_default()
4481                            .scrollbar
4482                            .get_or_insert_default()
4483                            .show = value;
4484                    },
4485                }),
4486                metadata: None,
4487                files: USER,
4488            }),
4489            SettingsPageItem::SettingItem(SettingItem {
4490                title: "Show Diagnostics",
4491                description: "Which files containing diagnostic errors/warnings to mark in the project panel.",
4492                field: Box::new(SettingField {
4493                    json_path: Some("project_panel.show_diagnostics"),
4494                    pick: |settings_content| {
4495                        settings_content
4496                            .project_panel
4497                            .as_ref()?
4498                            .show_diagnostics
4499                            .as_ref()
4500                    },
4501                    write: |settings_content, value| {
4502                        settings_content
4503                            .project_panel
4504                            .get_or_insert_default()
4505                            .show_diagnostics = value;
4506                    },
4507                }),
4508                metadata: None,
4509                files: USER,
4510            }),
4511            SettingsPageItem::SettingItem(SettingItem {
4512                title: "Sticky Scroll",
4513                description: "Whether to stick parent directories at top of the project panel.",
4514                field: Box::new(SettingField {
4515                    json_path: Some("project_panel.sticky_scroll"),
4516                    pick: |settings_content| {
4517                        settings_content
4518                            .project_panel
4519                            .as_ref()?
4520                            .sticky_scroll
4521                            .as_ref()
4522                    },
4523                    write: |settings_content, value| {
4524                        settings_content
4525                            .project_panel
4526                            .get_or_insert_default()
4527                            .sticky_scroll = value;
4528                    },
4529                }),
4530                metadata: None,
4531                files: USER,
4532            }),
4533            SettingsPageItem::SettingItem(SettingItem {
4534                files: USER,
4535                title: "Show Indent Guides",
4536                description: "Show indent guides in the project panel.",
4537                field: Box::new(SettingField {
4538                    json_path: Some("project_panel.indent_guides.show"),
4539                    pick: |settings_content| {
4540                        settings_content
4541                            .project_panel
4542                            .as_ref()?
4543                            .indent_guides
4544                            .as_ref()?
4545                            .show
4546                            .as_ref()
4547                    },
4548                    write: |settings_content, value| {
4549                        settings_content
4550                            .project_panel
4551                            .get_or_insert_default()
4552                            .indent_guides
4553                            .get_or_insert_default()
4554                            .show = value;
4555                    },
4556                }),
4557                metadata: None,
4558            }),
4559            SettingsPageItem::SettingItem(SettingItem {
4560                title: "Drag and Drop",
4561                description: "Whether to enable drag-and-drop operations in the project panel.",
4562                field: Box::new(SettingField {
4563                    json_path: Some("project_panel.drag_and_drop"),
4564                    pick: |settings_content| {
4565                        settings_content
4566                            .project_panel
4567                            .as_ref()?
4568                            .drag_and_drop
4569                            .as_ref()
4570                    },
4571                    write: |settings_content, value| {
4572                        settings_content
4573                            .project_panel
4574                            .get_or_insert_default()
4575                            .drag_and_drop = value;
4576                    },
4577                }),
4578                metadata: None,
4579                files: USER,
4580            }),
4581            SettingsPageItem::SettingItem(SettingItem {
4582                title: "Hide Root",
4583                description: "Whether to hide the root entry when only one folder is open in the window.",
4584                field: Box::new(SettingField {
4585                    json_path: Some("project_panel.drag_and_drop"),
4586                    pick: |settings_content| {
4587                        settings_content.project_panel.as_ref()?.hide_root.as_ref()
4588                    },
4589                    write: |settings_content, value| {
4590                        settings_content
4591                            .project_panel
4592                            .get_or_insert_default()
4593                            .hide_root = value;
4594                    },
4595                }),
4596                metadata: None,
4597                files: USER,
4598            }),
4599            SettingsPageItem::SettingItem(SettingItem {
4600                title: "Hide Hidden",
4601                description: "Whether to hide the hidden entries in the project panel.",
4602                field: Box::new(SettingField {
4603                    json_path: Some("project_panel.hide_hidden"),
4604                    pick: |settings_content| {
4605                        settings_content
4606                            .project_panel
4607                            .as_ref()?
4608                            .hide_hidden
4609                            .as_ref()
4610                    },
4611                    write: |settings_content, value| {
4612                        settings_content
4613                            .project_panel
4614                            .get_or_insert_default()
4615                            .hide_hidden = value;
4616                    },
4617                }),
4618                metadata: None,
4619                files: USER,
4620            }),
4621            SettingsPageItem::SettingItem(SettingItem {
4622                title: "Hidden Files",
4623                description: "Globs to match files that will be considered \"hidden\" and can be hidden from the project panel.",
4624                field: Box::new(
4625                    SettingField {
4626                        json_path: Some("worktree.hidden_files"),
4627                        pick: |settings_content| {
4628                            settings_content.project.worktree.hidden_files.as_ref()
4629                        },
4630                        write: |settings_content, value| {
4631                            settings_content.project.worktree.hidden_files = value;
4632                        },
4633                    }
4634                    .unimplemented(),
4635                ),
4636                metadata: None,
4637                files: USER,
4638            }),
4639        ]
4640    }
4641
4642    fn auto_open_files_section() -> [SettingsPageItem; 5] {
4643        [
4644            SettingsPageItem::SectionHeader("Auto Open Files"),
4645            SettingsPageItem::SettingItem(SettingItem {
4646                title: "On Create",
4647                description: "Whether to automatically open newly created files in the editor.",
4648                field: Box::new(SettingField {
4649                    json_path: Some("project_panel.auto_open.on_create"),
4650                    pick: |settings_content| {
4651                        settings_content
4652                            .project_panel
4653                            .as_ref()?
4654                            .auto_open
4655                            .as_ref()?
4656                            .on_create
4657                            .as_ref()
4658                    },
4659                    write: |settings_content, value| {
4660                        settings_content
4661                            .project_panel
4662                            .get_or_insert_default()
4663                            .auto_open
4664                            .get_or_insert_default()
4665                            .on_create = value;
4666                    },
4667                }),
4668                metadata: None,
4669                files: USER,
4670            }),
4671            SettingsPageItem::SettingItem(SettingItem {
4672                title: "On Paste",
4673                description: "Whether to automatically open files after pasting or duplicating them.",
4674                field: Box::new(SettingField {
4675                    json_path: Some("project_panel.auto_open.on_paste"),
4676                    pick: |settings_content| {
4677                        settings_content
4678                            .project_panel
4679                            .as_ref()?
4680                            .auto_open
4681                            .as_ref()?
4682                            .on_paste
4683                            .as_ref()
4684                    },
4685                    write: |settings_content, value| {
4686                        settings_content
4687                            .project_panel
4688                            .get_or_insert_default()
4689                            .auto_open
4690                            .get_or_insert_default()
4691                            .on_paste = value;
4692                    },
4693                }),
4694                metadata: None,
4695                files: USER,
4696            }),
4697            SettingsPageItem::SettingItem(SettingItem {
4698                title: "On Drop",
4699                description: "Whether to automatically open files dropped from external sources.",
4700                field: Box::new(SettingField {
4701                    json_path: Some("project_panel.auto_open.on_drop"),
4702                    pick: |settings_content| {
4703                        settings_content
4704                            .project_panel
4705                            .as_ref()?
4706                            .auto_open
4707                            .as_ref()?
4708                            .on_drop
4709                            .as_ref()
4710                    },
4711                    write: |settings_content, value| {
4712                        settings_content
4713                            .project_panel
4714                            .get_or_insert_default()
4715                            .auto_open
4716                            .get_or_insert_default()
4717                            .on_drop = value;
4718                    },
4719                }),
4720                metadata: None,
4721                files: USER,
4722            }),
4723            SettingsPageItem::SettingItem(SettingItem {
4724                title: "Sort Mode",
4725                description: "Sort order for entries in the project panel.",
4726                field: Box::new(SettingField {
4727                    pick: |settings_content| {
4728                        settings_content.project_panel.as_ref()?.sort_mode.as_ref()
4729                    },
4730                    write: |settings_content, value| {
4731                        settings_content
4732                            .project_panel
4733                            .get_or_insert_default()
4734                            .sort_mode = value;
4735                    },
4736                    json_path: Some("project_panel.sort_mode"),
4737                }),
4738                metadata: None,
4739                files: USER,
4740            }),
4741        ]
4742    }
4743
4744    fn terminal_panel_section() -> [SettingsPageItem; 2] {
4745        [
4746            SettingsPageItem::SectionHeader("Terminal Panel"),
4747            SettingsPageItem::SettingItem(SettingItem {
4748                title: "Terminal Dock",
4749                description: "Where to dock the terminal panel.",
4750                field: Box::new(SettingField {
4751                    json_path: Some("terminal.dock"),
4752                    pick: |settings_content| settings_content.terminal.as_ref()?.dock.as_ref(),
4753                    write: |settings_content, value| {
4754                        settings_content.terminal.get_or_insert_default().dock = value;
4755                    },
4756                }),
4757                metadata: None,
4758                files: USER,
4759            }),
4760        ]
4761    }
4762
4763    fn outline_panel_section() -> [SettingsPageItem; 11] {
4764        [
4765            SettingsPageItem::SectionHeader("Outline Panel"),
4766            SettingsPageItem::SettingItem(SettingItem {
4767                title: "Outline Panel Button",
4768                description: "Show the outline panel button in the status bar.",
4769                field: Box::new(SettingField {
4770                    json_path: Some("outline_panel.button"),
4771                    pick: |settings_content| {
4772                        settings_content.outline_panel.as_ref()?.button.as_ref()
4773                    },
4774                    write: |settings_content, value| {
4775                        settings_content
4776                            .outline_panel
4777                            .get_or_insert_default()
4778                            .button = value;
4779                    },
4780                }),
4781                metadata: None,
4782                files: USER,
4783            }),
4784            SettingsPageItem::SettingItem(SettingItem {
4785                title: "Outline Panel Dock",
4786                description: "Where to dock the outline panel.",
4787                field: Box::new(SettingField {
4788                    json_path: Some("outline_panel.dock"),
4789                    pick: |settings_content| settings_content.outline_panel.as_ref()?.dock.as_ref(),
4790                    write: |settings_content, value| {
4791                        settings_content.outline_panel.get_or_insert_default().dock = value;
4792                    },
4793                }),
4794                metadata: None,
4795                files: USER,
4796            }),
4797            SettingsPageItem::SettingItem(SettingItem {
4798                title: "Outline Panel Default Width",
4799                description: "Default width of the outline panel in pixels.",
4800                field: Box::new(SettingField {
4801                    json_path: Some("outline_panel.default_width"),
4802                    pick: |settings_content| {
4803                        settings_content
4804                            .outline_panel
4805                            .as_ref()?
4806                            .default_width
4807                            .as_ref()
4808                    },
4809                    write: |settings_content, value| {
4810                        settings_content
4811                            .outline_panel
4812                            .get_or_insert_default()
4813                            .default_width = value;
4814                    },
4815                }),
4816                metadata: None,
4817                files: USER,
4818            }),
4819            SettingsPageItem::SettingItem(SettingItem {
4820                title: "File Icons",
4821                description: "Show file icons in the outline panel.",
4822                field: Box::new(SettingField {
4823                    json_path: Some("outline_panel.file_icons"),
4824                    pick: |settings_content| {
4825                        settings_content.outline_panel.as_ref()?.file_icons.as_ref()
4826                    },
4827                    write: |settings_content, value| {
4828                        settings_content
4829                            .outline_panel
4830                            .get_or_insert_default()
4831                            .file_icons = value;
4832                    },
4833                }),
4834                metadata: None,
4835                files: USER,
4836            }),
4837            SettingsPageItem::SettingItem(SettingItem {
4838                title: "Folder Icons",
4839                description: "Whether to show folder icons or chevrons for directories in the outline panel.",
4840                field: Box::new(SettingField {
4841                    json_path: Some("outline_panel.folder_icons"),
4842                    pick: |settings_content| {
4843                        settings_content
4844                            .outline_panel
4845                            .as_ref()?
4846                            .folder_icons
4847                            .as_ref()
4848                    },
4849                    write: |settings_content, value| {
4850                        settings_content
4851                            .outline_panel
4852                            .get_or_insert_default()
4853                            .folder_icons = value;
4854                    },
4855                }),
4856                metadata: None,
4857                files: USER,
4858            }),
4859            SettingsPageItem::SettingItem(SettingItem {
4860                title: "Git Status",
4861                description: "Show the Git status in the outline panel.",
4862                field: Box::new(SettingField {
4863                    json_path: Some("outline_panel.git_status"),
4864                    pick: |settings_content| {
4865                        settings_content.outline_panel.as_ref()?.git_status.as_ref()
4866                    },
4867                    write: |settings_content, value| {
4868                        settings_content
4869                            .outline_panel
4870                            .get_or_insert_default()
4871                            .git_status = value;
4872                    },
4873                }),
4874                metadata: None,
4875                files: USER,
4876            }),
4877            SettingsPageItem::SettingItem(SettingItem {
4878                title: "Indent Size",
4879                description: "Amount of indentation for nested items.",
4880                field: Box::new(SettingField {
4881                    json_path: Some("outline_panel.indent_size"),
4882                    pick: |settings_content| {
4883                        settings_content
4884                            .outline_panel
4885                            .as_ref()?
4886                            .indent_size
4887                            .as_ref()
4888                    },
4889                    write: |settings_content, value| {
4890                        settings_content
4891                            .outline_panel
4892                            .get_or_insert_default()
4893                            .indent_size = value;
4894                    },
4895                }),
4896                metadata: None,
4897                files: USER,
4898            }),
4899            SettingsPageItem::SettingItem(SettingItem {
4900                title: "Auto Reveal Entries",
4901                description: "Whether to reveal when a corresponding outline entry becomes active.",
4902                field: Box::new(SettingField {
4903                    json_path: Some("outline_panel.auto_reveal_entries"),
4904                    pick: |settings_content| {
4905                        settings_content
4906                            .outline_panel
4907                            .as_ref()?
4908                            .auto_reveal_entries
4909                            .as_ref()
4910                    },
4911                    write: |settings_content, value| {
4912                        settings_content
4913                            .outline_panel
4914                            .get_or_insert_default()
4915                            .auto_reveal_entries = value;
4916                    },
4917                }),
4918                metadata: None,
4919                files: USER,
4920            }),
4921            SettingsPageItem::SettingItem(SettingItem {
4922                title: "Auto Fold Directories",
4923                description: "Whether to fold directories automatically when a directory contains only one subdirectory.",
4924                field: Box::new(SettingField {
4925                    json_path: Some("outline_panel.auto_fold_dirs"),
4926                    pick: |settings_content| {
4927                        settings_content
4928                            .outline_panel
4929                            .as_ref()?
4930                            .auto_fold_dirs
4931                            .as_ref()
4932                    },
4933                    write: |settings_content, value| {
4934                        settings_content
4935                            .outline_panel
4936                            .get_or_insert_default()
4937                            .auto_fold_dirs = value;
4938                    },
4939                }),
4940                metadata: None,
4941                files: USER,
4942            }),
4943            SettingsPageItem::SettingItem(SettingItem {
4944                files: USER,
4945                title: "Show Indent Guides",
4946                description: "When to show indent guides in the outline panel.",
4947                field: Box::new(SettingField {
4948                    json_path: Some("outline_panel.indent_guides.show"),
4949                    pick: |settings_content| {
4950                        settings_content
4951                            .outline_panel
4952                            .as_ref()?
4953                            .indent_guides
4954                            .as_ref()?
4955                            .show
4956                            .as_ref()
4957                    },
4958                    write: |settings_content, value| {
4959                        settings_content
4960                            .outline_panel
4961                            .get_or_insert_default()
4962                            .indent_guides
4963                            .get_or_insert_default()
4964                            .show = value;
4965                    },
4966                }),
4967                metadata: None,
4968            }),
4969        ]
4970    }
4971
4972    fn git_panel_section() -> [SettingsPageItem; 10] {
4973        [
4974            SettingsPageItem::SectionHeader("Git Panel"),
4975            SettingsPageItem::SettingItem(SettingItem {
4976                title: "Git Panel Button",
4977                description: "Show the Git panel button in the status bar.",
4978                field: Box::new(SettingField {
4979                    json_path: Some("git_panel.button"),
4980                    pick: |settings_content| settings_content.git_panel.as_ref()?.button.as_ref(),
4981                    write: |settings_content, value| {
4982                        settings_content.git_panel.get_or_insert_default().button = value;
4983                    },
4984                }),
4985                metadata: None,
4986                files: USER,
4987            }),
4988            SettingsPageItem::SettingItem(SettingItem {
4989                title: "Git Panel Dock",
4990                description: "Where to dock the Git panel.",
4991                field: Box::new(SettingField {
4992                    json_path: Some("git_panel.dock"),
4993                    pick: |settings_content| settings_content.git_panel.as_ref()?.dock.as_ref(),
4994                    write: |settings_content, value| {
4995                        settings_content.git_panel.get_or_insert_default().dock = value;
4996                    },
4997                }),
4998                metadata: None,
4999                files: USER,
5000            }),
5001            SettingsPageItem::SettingItem(SettingItem {
5002                title: "Git Panel Default Width",
5003                description: "Default width of the Git panel in pixels.",
5004                field: Box::new(SettingField {
5005                    json_path: Some("git_panel.default_width"),
5006                    pick: |settings_content| {
5007                        settings_content.git_panel.as_ref()?.default_width.as_ref()
5008                    },
5009                    write: |settings_content, value| {
5010                        settings_content
5011                            .git_panel
5012                            .get_or_insert_default()
5013                            .default_width = value;
5014                    },
5015                }),
5016                metadata: None,
5017                files: USER,
5018            }),
5019            SettingsPageItem::SettingItem(SettingItem {
5020                title: "Git Panel Status Style",
5021                description: "How entry statuses are displayed.",
5022                field: Box::new(SettingField {
5023                    json_path: Some("git_panel.status_style"),
5024                    pick: |settings_content| {
5025                        settings_content.git_panel.as_ref()?.status_style.as_ref()
5026                    },
5027                    write: |settings_content, value| {
5028                        settings_content
5029                            .git_panel
5030                            .get_or_insert_default()
5031                            .status_style = value;
5032                    },
5033                }),
5034                metadata: None,
5035                files: USER,
5036            }),
5037            SettingsPageItem::SettingItem(SettingItem {
5038                title: "Fallback Branch Name",
5039                description: "Default branch name will be when init.defaultbranch is not set in Git.",
5040                field: Box::new(SettingField {
5041                    json_path: Some("git_panel.fallback_branch_name"),
5042                    pick: |settings_content| {
5043                        settings_content
5044                            .git_panel
5045                            .as_ref()?
5046                            .fallback_branch_name
5047                            .as_ref()
5048                    },
5049                    write: |settings_content, value| {
5050                        settings_content
5051                            .git_panel
5052                            .get_or_insert_default()
5053                            .fallback_branch_name = value;
5054                    },
5055                }),
5056                metadata: None,
5057                files: USER,
5058            }),
5059            SettingsPageItem::SettingItem(SettingItem {
5060                title: "Sort By Path",
5061                description: "Enable to sort entries in the panel by path, disable to sort by status.",
5062                field: Box::new(SettingField {
5063                    json_path: Some("git_panel.sort_by_path"),
5064                    pick: |settings_content| {
5065                        settings_content.git_panel.as_ref()?.sort_by_path.as_ref()
5066                    },
5067                    write: |settings_content, value| {
5068                        settings_content
5069                            .git_panel
5070                            .get_or_insert_default()
5071                            .sort_by_path = value;
5072                    },
5073                }),
5074                metadata: None,
5075                files: USER,
5076            }),
5077            SettingsPageItem::SettingItem(SettingItem {
5078                title: "Collapse Untracked Diff",
5079                description: "Whether to collapse untracked files in the diff panel.",
5080                field: Box::new(SettingField {
5081                    json_path: Some("git_panel.collapse_untracked_diff"),
5082                    pick: |settings_content| {
5083                        settings_content
5084                            .git_panel
5085                            .as_ref()?
5086                            .collapse_untracked_diff
5087                            .as_ref()
5088                    },
5089                    write: |settings_content, value| {
5090                        settings_content
5091                            .git_panel
5092                            .get_or_insert_default()
5093                            .collapse_untracked_diff = value;
5094                    },
5095                }),
5096                metadata: None,
5097                files: USER,
5098            }),
5099            SettingsPageItem::SettingItem(SettingItem {
5100                title: "Tree View",
5101                description: "Enable to show entries in tree view list, disable to show in flat view list.",
5102                field: Box::new(SettingField {
5103                    json_path: Some("git_panel.tree_view"),
5104                    pick: |settings_content| {
5105                        settings_content.git_panel.as_ref()?.tree_view.as_ref()
5106                    },
5107                    write: |settings_content, value| {
5108                        settings_content.git_panel.get_or_insert_default().tree_view = value;
5109                    },
5110                }),
5111                metadata: None,
5112                files: USER,
5113            }),
5114            SettingsPageItem::SettingItem(SettingItem {
5115                title: "Scroll Bar",
5116                description: "How and when the scrollbar should be displayed.",
5117                field: Box::new(SettingField {
5118                    json_path: Some("git_panel.scrollbar.show"),
5119                    pick: |settings_content| {
5120                        show_scrollbar_or_editor(settings_content, |settings_content| {
5121                            settings_content
5122                                .git_panel
5123                                .as_ref()?
5124                                .scrollbar
5125                                .as_ref()?
5126                                .show
5127                                .as_ref()
5128                        })
5129                    },
5130                    write: |settings_content, value| {
5131                        settings_content
5132                            .git_panel
5133                            .get_or_insert_default()
5134                            .scrollbar
5135                            .get_or_insert_default()
5136                            .show = value;
5137                    },
5138                }),
5139                metadata: None,
5140                files: USER,
5141            }),
5142        ]
5143    }
5144
5145    fn debugger_panel_section() -> [SettingsPageItem; 2] {
5146        [
5147            SettingsPageItem::SectionHeader("Debugger Panel"),
5148            SettingsPageItem::SettingItem(SettingItem {
5149                title: "Debugger Panel Dock",
5150                description: "The dock position of the debug panel.",
5151                field: Box::new(SettingField {
5152                    json_path: Some("debugger.dock"),
5153                    pick: |settings_content| settings_content.debugger.as_ref()?.dock.as_ref(),
5154                    write: |settings_content, value| {
5155                        settings_content.debugger.get_or_insert_default().dock = value;
5156                    },
5157                }),
5158                metadata: None,
5159                files: USER,
5160            }),
5161        ]
5162    }
5163
5164    fn notification_panel_section() -> [SettingsPageItem; 4] {
5165        [
5166            SettingsPageItem::SectionHeader("Notification Panel"),
5167            SettingsPageItem::SettingItem(SettingItem {
5168                title: "Notification Panel Button",
5169                description: "Show the notification panel button in the status bar.",
5170                field: Box::new(SettingField {
5171                    json_path: Some("notification_panel.button"),
5172                    pick: |settings_content| {
5173                        settings_content
5174                            .notification_panel
5175                            .as_ref()?
5176                            .button
5177                            .as_ref()
5178                    },
5179                    write: |settings_content, value| {
5180                        settings_content
5181                            .notification_panel
5182                            .get_or_insert_default()
5183                            .button = value;
5184                    },
5185                }),
5186                metadata: None,
5187                files: USER,
5188            }),
5189            SettingsPageItem::SettingItem(SettingItem {
5190                title: "Notification Panel Dock",
5191                description: "Where to dock the notification panel.",
5192                field: Box::new(SettingField {
5193                    json_path: Some("notification_panel.dock"),
5194                    pick: |settings_content| {
5195                        settings_content.notification_panel.as_ref()?.dock.as_ref()
5196                    },
5197                    write: |settings_content, value| {
5198                        settings_content
5199                            .notification_panel
5200                            .get_or_insert_default()
5201                            .dock = value;
5202                    },
5203                }),
5204                metadata: None,
5205                files: USER,
5206            }),
5207            SettingsPageItem::SettingItem(SettingItem {
5208                title: "Notification Panel Default Width",
5209                description: "Default width of the notification panel in pixels.",
5210                field: Box::new(SettingField {
5211                    json_path: Some("notification_panel.default_width"),
5212                    pick: |settings_content| {
5213                        settings_content
5214                            .notification_panel
5215                            .as_ref()?
5216                            .default_width
5217                            .as_ref()
5218                    },
5219                    write: |settings_content, value| {
5220                        settings_content
5221                            .notification_panel
5222                            .get_or_insert_default()
5223                            .default_width = value;
5224                    },
5225                }),
5226                metadata: None,
5227                files: USER,
5228            }),
5229        ]
5230    }
5231
5232    fn collaboration_panel_section() -> [SettingsPageItem; 4] {
5233        [
5234            SettingsPageItem::SectionHeader("Collaboration Panel"),
5235            SettingsPageItem::SettingItem(SettingItem {
5236                title: "Collaboration Panel Button",
5237                description: "Show the collaboration panel button in the status bar.",
5238                field: Box::new(SettingField {
5239                    json_path: Some("collaboration_panel.button"),
5240                    pick: |settings_content| {
5241                        settings_content
5242                            .collaboration_panel
5243                            .as_ref()?
5244                            .button
5245                            .as_ref()
5246                    },
5247                    write: |settings_content, value| {
5248                        settings_content
5249                            .collaboration_panel
5250                            .get_or_insert_default()
5251                            .button = value;
5252                    },
5253                }),
5254                metadata: None,
5255                files: USER,
5256            }),
5257            SettingsPageItem::SettingItem(SettingItem {
5258                title: "Collaboration Panel Dock",
5259                description: "Where to dock the collaboration panel.",
5260                field: Box::new(SettingField {
5261                    json_path: Some("collaboration_panel.dock"),
5262                    pick: |settings_content| {
5263                        settings_content.collaboration_panel.as_ref()?.dock.as_ref()
5264                    },
5265                    write: |settings_content, value| {
5266                        settings_content
5267                            .collaboration_panel
5268                            .get_or_insert_default()
5269                            .dock = value;
5270                    },
5271                }),
5272                metadata: None,
5273                files: USER,
5274            }),
5275            SettingsPageItem::SettingItem(SettingItem {
5276                title: "Collaboration Panel Default Width",
5277                description: "Default width of the collaboration panel in pixels.",
5278                field: Box::new(SettingField {
5279                    json_path: Some("collaboration_panel.dock"),
5280                    pick: |settings_content| {
5281                        settings_content
5282                            .collaboration_panel
5283                            .as_ref()?
5284                            .default_width
5285                            .as_ref()
5286                    },
5287                    write: |settings_content, value| {
5288                        settings_content
5289                            .collaboration_panel
5290                            .get_or_insert_default()
5291                            .default_width = value;
5292                    },
5293                }),
5294                metadata: None,
5295                files: USER,
5296            }),
5297        ]
5298    }
5299
5300    fn agent_panel_section() -> [SettingsPageItem; 5] {
5301        [
5302            SettingsPageItem::SectionHeader("Agent Panel"),
5303            SettingsPageItem::SettingItem(SettingItem {
5304                title: "Agent Panel Button",
5305                description: "Whether to show the agent panel button in the status bar.",
5306                field: Box::new(SettingField {
5307                    json_path: Some("agent.button"),
5308                    pick: |settings_content| settings_content.agent.as_ref()?.button.as_ref(),
5309                    write: |settings_content, value| {
5310                        settings_content.agent.get_or_insert_default().button = value;
5311                    },
5312                }),
5313                metadata: None,
5314                files: USER,
5315            }),
5316            SettingsPageItem::SettingItem(SettingItem {
5317                title: "Agent Panel Dock",
5318                description: "Where to dock the agent panel.",
5319                field: Box::new(SettingField {
5320                    json_path: Some("agent.dock"),
5321                    pick: |settings_content| settings_content.agent.as_ref()?.dock.as_ref(),
5322                    write: |settings_content, value| {
5323                        settings_content.agent.get_or_insert_default().dock = value;
5324                    },
5325                }),
5326                metadata: None,
5327                files: USER,
5328            }),
5329            SettingsPageItem::SettingItem(SettingItem {
5330                title: "Agent Panel Default Width",
5331                description: "Default width when the agent panel is docked to the left or right.",
5332                field: Box::new(SettingField {
5333                    json_path: Some("agent.default_width"),
5334                    pick: |settings_content| {
5335                        settings_content.agent.as_ref()?.default_width.as_ref()
5336                    },
5337                    write: |settings_content, value| {
5338                        settings_content.agent.get_or_insert_default().default_width = value;
5339                    },
5340                }),
5341                metadata: None,
5342                files: USER,
5343            }),
5344            SettingsPageItem::SettingItem(SettingItem {
5345                title: "Agent Panel Default Height",
5346                description: "Default height when the agent panel is docked to the bottom.",
5347                field: Box::new(SettingField {
5348                    json_path: Some("agent.default_height"),
5349                    pick: |settings_content| {
5350                        settings_content.agent.as_ref()?.default_height.as_ref()
5351                    },
5352                    write: |settings_content, value| {
5353                        settings_content
5354                            .agent
5355                            .get_or_insert_default()
5356                            .default_height = value;
5357                    },
5358                }),
5359                metadata: None,
5360                files: USER,
5361            }),
5362        ]
5363    }
5364
5365    SettingsPage {
5366        title: "Panels",
5367        items: concat_sections![
5368            project_panel_section(),
5369            auto_open_files_section(),
5370            terminal_panel_section(),
5371            outline_panel_section(),
5372            git_panel_section(),
5373            debugger_panel_section(),
5374            notification_panel_section(),
5375            collaboration_panel_section(),
5376            agent_panel_section(),
5377        ],
5378    }
5379}
5380
5381fn debugger_page() -> SettingsPage {
5382    fn general_section() -> [SettingsPageItem; 6] {
5383        [
5384            SettingsPageItem::SectionHeader("General"),
5385            SettingsPageItem::SettingItem(SettingItem {
5386                title: "Stepping Granularity",
5387                description: "Determines the stepping granularity for debug operations.",
5388                field: Box::new(SettingField {
5389                    json_path: Some("debugger.stepping_granularity"),
5390                    pick: |settings_content| {
5391                        settings_content
5392                            .debugger
5393                            .as_ref()?
5394                            .stepping_granularity
5395                            .as_ref()
5396                    },
5397                    write: |settings_content, value| {
5398                        settings_content
5399                            .debugger
5400                            .get_or_insert_default()
5401                            .stepping_granularity = value;
5402                    },
5403                }),
5404                metadata: None,
5405                files: USER,
5406            }),
5407            SettingsPageItem::SettingItem(SettingItem {
5408                title: "Save Breakpoints",
5409                description: "Whether breakpoints should be reused across Zed sessions.",
5410                field: Box::new(SettingField {
5411                    json_path: Some("debugger.save_breakpoints"),
5412                    pick: |settings_content| {
5413                        settings_content
5414                            .debugger
5415                            .as_ref()?
5416                            .save_breakpoints
5417                            .as_ref()
5418                    },
5419                    write: |settings_content, value| {
5420                        settings_content
5421                            .debugger
5422                            .get_or_insert_default()
5423                            .save_breakpoints = value;
5424                    },
5425                }),
5426                metadata: None,
5427                files: USER,
5428            }),
5429            SettingsPageItem::SettingItem(SettingItem {
5430                title: "Timeout",
5431                description: "Time in milliseconds until timeout error when connecting to a TCP debug adapter.",
5432                field: Box::new(SettingField {
5433                    json_path: Some("debugger.timeout"),
5434                    pick: |settings_content| settings_content.debugger.as_ref()?.timeout.as_ref(),
5435                    write: |settings_content, value| {
5436                        settings_content.debugger.get_or_insert_default().timeout = value;
5437                    },
5438                }),
5439                metadata: None,
5440                files: USER,
5441            }),
5442            SettingsPageItem::SettingItem(SettingItem {
5443                title: "Log DAP Communications",
5444                description: "Whether to log messages between active debug adapters and Zed.",
5445                field: Box::new(SettingField {
5446                    json_path: Some("debugger.log_dap_communications"),
5447                    pick: |settings_content| {
5448                        settings_content
5449                            .debugger
5450                            .as_ref()?
5451                            .log_dap_communications
5452                            .as_ref()
5453                    },
5454                    write: |settings_content, value| {
5455                        settings_content
5456                            .debugger
5457                            .get_or_insert_default()
5458                            .log_dap_communications = value;
5459                    },
5460                }),
5461                metadata: None,
5462                files: USER,
5463            }),
5464            SettingsPageItem::SettingItem(SettingItem {
5465                title: "Format DAP Log Messages",
5466                description: "Whether to format DAP messages when adding them to debug adapter logger.",
5467                field: Box::new(SettingField {
5468                    json_path: Some("debugger.format_dap_log_messages"),
5469                    pick: |settings_content| {
5470                        settings_content
5471                            .debugger
5472                            .as_ref()?
5473                            .format_dap_log_messages
5474                            .as_ref()
5475                    },
5476                    write: |settings_content, value| {
5477                        settings_content
5478                            .debugger
5479                            .get_or_insert_default()
5480                            .format_dap_log_messages = value;
5481                    },
5482                }),
5483                metadata: None,
5484                files: USER,
5485            }),
5486        ]
5487    }
5488
5489    SettingsPage {
5490        title: "Debugger",
5491        items: concat_sections![general_section()],
5492    }
5493}
5494
5495fn terminal_page() -> SettingsPage {
5496    fn environment_section() -> [SettingsPageItem; 5] {
5497        [
5498                SettingsPageItem::SectionHeader("Environment"),
5499                SettingsPageItem::DynamicItem(DynamicItem {
5500                    discriminant: SettingItem {
5501                        files: USER | PROJECT,
5502                        title: "Shell",
5503                        description: "What shell to use when opening a terminal.",
5504                        field: Box::new(SettingField {
5505                            json_path: Some("terminal.shell$"),
5506                            pick: |settings_content| {
5507                                Some(&dynamic_variants::<settings::Shell>()[
5508                                    settings_content
5509                                        .terminal
5510                                        .as_ref()?
5511                                        .project
5512                                        .shell
5513                                        .as_ref()?
5514                                        .discriminant() as usize
5515                                ])
5516                            },
5517                            write: |settings_content, value| {
5518                                let Some(value) = value else {
5519                                    if let Some(terminal) = settings_content.terminal.as_mut() {
5520                                        terminal.project.shell = None;
5521                                    }
5522                                    return;
5523                                };
5524                                let settings_value = settings_content
5525                                    .terminal
5526                                    .get_or_insert_default()
5527                                    .project
5528                                    .shell
5529                                    .get_or_insert_with(|| settings::Shell::default());
5530                                let default_shell = if cfg!(target_os = "windows") {
5531                                    "powershell.exe"
5532                                } else {
5533                                    "sh"
5534                                };
5535                                *settings_value = match value {
5536                                    settings::ShellDiscriminants::System => settings::Shell::System,
5537                                    settings::ShellDiscriminants::Program => {
5538                                        let program = match settings_value {
5539                                            settings::Shell::Program(program) => program.clone(),
5540                                            settings::Shell::WithArguments { program, .. } => program.clone(),
5541                                            _ => String::from(default_shell),
5542                                        };
5543                                        settings::Shell::Program(program)
5544                                    }
5545                                    settings::ShellDiscriminants::WithArguments => {
5546                                        let (program, args, title_override) = match settings_value {
5547                                            settings::Shell::Program(program) => (program.clone(), vec![], None),
5548                                            settings::Shell::WithArguments {
5549                                                program,
5550                                                args,
5551                                                title_override,
5552                                            } => (program.clone(), args.clone(), title_override.clone()),
5553                                            _ => (String::from(default_shell), vec![], None),
5554                                        };
5555                                        settings::Shell::WithArguments {
5556                                            program,
5557                                            args,
5558                                            title_override,
5559                                        }
5560                                    }
5561                                };
5562                            },
5563                        }),
5564                        metadata: None,
5565                    },
5566                    pick_discriminant: |settings_content| {
5567                        Some(
5568                            settings_content
5569                                .terminal
5570                                .as_ref()?
5571                                .project
5572                                .shell
5573                                .as_ref()?
5574                                .discriminant() as usize,
5575                        )
5576                    },
5577                    fields: dynamic_variants::<settings::Shell>()
5578                        .into_iter()
5579                        .map(|variant| match variant {
5580                            settings::ShellDiscriminants::System => vec![],
5581                            settings::ShellDiscriminants::Program => vec![SettingItem {
5582                                files: USER | PROJECT,
5583                                title: "Program",
5584                                description: "The shell program to use.",
5585                                field: Box::new(SettingField {
5586                                    json_path: Some("terminal.shell"),
5587                                    pick: |settings_content| match settings_content.terminal.as_ref()?.project.shell.as_ref()
5588                                    {
5589                                        Some(settings::Shell::Program(program)) => Some(program),
5590                                        _ => None,
5591                                    },
5592                                    write: |settings_content, value| {
5593                                        let Some(value) = value else {
5594                                            return;
5595                                        };
5596                                        match settings_content
5597                                            .terminal
5598                                            .get_or_insert_default()
5599                                            .project
5600                                            .shell
5601                                            .as_mut()
5602                                        {
5603                                            Some(settings::Shell::Program(program)) => *program = value,
5604                                            _ => return,
5605                                        }
5606                                    },
5607                                }),
5608                                metadata: None,
5609                            }],
5610                            settings::ShellDiscriminants::WithArguments => vec![
5611                                SettingItem {
5612                                    files: USER | PROJECT,
5613                                    title: "Program",
5614                                    description: "The shell program to run.",
5615                                    field: Box::new(SettingField {
5616                                        json_path: Some("terminal.shell.program"),
5617                                        pick: |settings_content| {
5618                                            match settings_content.terminal.as_ref()?.project.shell.as_ref() {
5619                                                Some(settings::Shell::WithArguments { program, .. }) => Some(program),
5620                                                _ => None,
5621                                            }
5622                                        },
5623                                        write: |settings_content, value| {
5624                                            let Some(value) = value else {
5625                                                return;
5626                                            };
5627                                            match settings_content
5628                                                .terminal
5629                                                .get_or_insert_default()
5630                                                .project
5631                                                .shell
5632                                                .as_mut()
5633                                            {
5634                                                Some(settings::Shell::WithArguments { program, .. }) => {
5635                                                    *program = value
5636                                                }
5637                                                _ => return,
5638                                            }
5639                                        },
5640                                    }),
5641                                    metadata: None,
5642                                },
5643                                SettingItem {
5644                                    files: USER | PROJECT,
5645                                    title: "Arguments",
5646                                    description: "The arguments to pass to the shell program.",
5647                                    field: Box::new(
5648                                        SettingField {
5649                                            json_path: Some("terminal.shell.args"),
5650                                            pick: |settings_content| {
5651                                                match settings_content.terminal.as_ref()?.project.shell.as_ref() {
5652                                                    Some(settings::Shell::WithArguments { args, .. }) => Some(args),
5653                                                    _ => None,
5654                                                }
5655                                            },
5656                                            write: |settings_content, value| {
5657                                                let Some(value) = value else {
5658                                                    return;
5659                                                };
5660                                                match settings_content
5661                                                    .terminal
5662                                                    .get_or_insert_default()
5663                                                    .project
5664                                                    .shell
5665                                                    .as_mut()
5666                                                {
5667                                                    Some(settings::Shell::WithArguments { args, .. }) => *args = value,
5668                                                    _ => return,
5669                                                }
5670                                            },
5671                                        }
5672                                        .unimplemented(),
5673                                    ),
5674                                    metadata: None,
5675                                },
5676                                SettingItem {
5677                                    files: USER | PROJECT,
5678                                    title: "Title Override",
5679                                    description: "An optional string to override the title of the terminal tab.",
5680                                    field: Box::new(SettingField {
5681                                        json_path: Some("terminal.shell.title_override"),
5682                                        pick: |settings_content| {
5683                                            match settings_content.terminal.as_ref()?.project.shell.as_ref() {
5684                                                Some(settings::Shell::WithArguments { title_override, .. }) => {
5685                                                    title_override.as_ref().or(DEFAULT_EMPTY_STRING)
5686                                                }
5687                                                _ => None,
5688                                            }
5689                                        },
5690                                        write: |settings_content, value| {
5691                                            match settings_content
5692                                                .terminal
5693                                                .get_or_insert_default()
5694                                                .project
5695                                                .shell
5696                                                .as_mut()
5697                                            {
5698                                                Some(settings::Shell::WithArguments { title_override, .. }) => {
5699                                                    *title_override = value.filter(|s| !s.is_empty())
5700                                                }
5701                                                _ => return,
5702                                            }
5703                                        },
5704                                    }),
5705                                    metadata: None,
5706                                },
5707                            ],
5708                        })
5709                        .collect(),
5710                }),
5711                SettingsPageItem::DynamicItem(DynamicItem {
5712                    discriminant: SettingItem {
5713                        files: USER | PROJECT,
5714                        title: "Working Directory",
5715                        description: "What working directory to use when launching the terminal.",
5716                        field: Box::new(SettingField {
5717                            json_path: Some("terminal.working_directory$"),
5718                            pick: |settings_content| {
5719                                Some(&dynamic_variants::<settings::WorkingDirectory>()[
5720                                    settings_content
5721                                        .terminal
5722                                        .as_ref()?
5723                                        .project
5724                                        .working_directory
5725                                        .as_ref()?
5726                                        .discriminant() as usize
5727                                ])
5728                            },
5729                            write: |settings_content, value| {
5730                                let Some(value) = value else {
5731                                    if let Some(terminal) = settings_content.terminal.as_mut() {
5732                                        terminal.project.working_directory = None;
5733                                    }
5734                                    return;
5735                                };
5736                                let settings_value = settings_content
5737                                    .terminal
5738                                    .get_or_insert_default()
5739                                    .project
5740                                    .working_directory
5741                                    .get_or_insert_with(|| settings::WorkingDirectory::CurrentProjectDirectory);
5742                                *settings_value = match value {
5743                                    settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => {
5744                                        settings::WorkingDirectory::CurrentProjectDirectory
5745                                    }
5746                                    settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => {
5747                                        settings::WorkingDirectory::FirstProjectDirectory
5748                                    }
5749                                    settings::WorkingDirectoryDiscriminants::AlwaysHome => {
5750                                        settings::WorkingDirectory::AlwaysHome
5751                                    }
5752                                    settings::WorkingDirectoryDiscriminants::Always => {
5753                                        let directory = match settings_value {
5754                                            settings::WorkingDirectory::Always { .. } => return,
5755                                            _ => String::new(),
5756                                        };
5757                                        settings::WorkingDirectory::Always { directory }
5758                                    }
5759                                };
5760                            },
5761                        }),
5762                        metadata: None,
5763                    },
5764                    pick_discriminant: |settings_content| {
5765                        Some(
5766                            settings_content
5767                                .terminal
5768                                .as_ref()?
5769                                .project
5770                                .working_directory
5771                                .as_ref()?
5772                                .discriminant() as usize,
5773                        )
5774                    },
5775                    fields: dynamic_variants::<settings::WorkingDirectory>()
5776                        .into_iter()
5777                        .map(|variant| match variant {
5778                            settings::WorkingDirectoryDiscriminants::CurrentProjectDirectory => vec![],
5779                            settings::WorkingDirectoryDiscriminants::FirstProjectDirectory => vec![],
5780                            settings::WorkingDirectoryDiscriminants::AlwaysHome => vec![],
5781                            settings::WorkingDirectoryDiscriminants::Always => vec![SettingItem {
5782                                files: USER | PROJECT,
5783                                title: "Directory",
5784                                description: "The directory path to use (will be shell expanded).",
5785                                field: Box::new(SettingField {
5786                                    json_path: Some("terminal.working_directory.always"),
5787                                    pick: |settings_content| {
5788                                        match settings_content.terminal.as_ref()?.project.working_directory.as_ref() {
5789                                            Some(settings::WorkingDirectory::Always { directory }) => Some(directory),
5790                                            _ => None,
5791                                        }
5792                                    },
5793                                    write: |settings_content, value| {
5794                                        let value = value.unwrap_or_default();
5795                                        match settings_content
5796                                            .terminal
5797                                            .get_or_insert_default()
5798                                            .project
5799                                            .working_directory
5800                                            .as_mut()
5801                                        {
5802                                            Some(settings::WorkingDirectory::Always { directory }) => *directory = value,
5803                                            _ => return,
5804                                        }
5805                                    },
5806                                }),
5807                                metadata: None,
5808                            }],
5809                        })
5810                        .collect(),
5811                }),
5812                SettingsPageItem::SettingItem(SettingItem {
5813                    title: "Environment Variables",
5814                    description: "Key-value pairs to add to the terminal's environment.",
5815                    field: Box::new(
5816                        SettingField {
5817                            json_path: Some("terminal.env"),
5818                            pick: |settings_content| settings_content.terminal.as_ref()?.project.env.as_ref(),
5819                            write: |settings_content, value| {
5820                                settings_content.terminal.get_or_insert_default().project.env = value;
5821                            },
5822                        }
5823                        .unimplemented(),
5824                    ),
5825                    metadata: None,
5826                    files: USER | PROJECT,
5827                }),
5828                SettingsPageItem::SettingItem(SettingItem {
5829                    title: "Detect Virtual Environment",
5830                    description: "Activates the Python virtual environment, if one is found, in the terminal's working directory.",
5831                    field: Box::new(
5832                        SettingField {
5833                            json_path: Some("terminal.detect_venv"),
5834                            pick: |settings_content| settings_content.terminal.as_ref()?.project.detect_venv.as_ref(),
5835                            write: |settings_content, value| {
5836                                settings_content
5837                                    .terminal
5838                                    .get_or_insert_default()
5839                                    .project
5840                                    .detect_venv = value;
5841                            },
5842                        }
5843                        .unimplemented(),
5844                    ),
5845                    metadata: None,
5846                    files: USER | PROJECT,
5847                }),
5848            ]
5849    }
5850
5851    fn font_section() -> [SettingsPageItem; 6] {
5852        [
5853            SettingsPageItem::SectionHeader("Font"),
5854            SettingsPageItem::SettingItem(SettingItem {
5855                title: "Font Size",
5856                description: "Font size for terminal text. If not set, defaults to buffer font size.",
5857                field: Box::new(SettingField {
5858                    json_path: Some("terminal.font_size"),
5859                    pick: |settings_content| {
5860                        settings_content
5861                            .terminal
5862                            .as_ref()
5863                            .and_then(|terminal| terminal.font_size.as_ref())
5864                            .or(settings_content.theme.buffer_font_size.as_ref())
5865                    },
5866                    write: |settings_content, value| {
5867                        settings_content.terminal.get_or_insert_default().font_size = value;
5868                    },
5869                }),
5870                metadata: None,
5871                files: USER,
5872            }),
5873            SettingsPageItem::SettingItem(SettingItem {
5874                title: "Font Family",
5875                description: "Font family for terminal text. If not set, defaults to buffer font family.",
5876                field: Box::new(SettingField {
5877                    json_path: Some("terminal.font_family"),
5878                    pick: |settings_content| {
5879                        settings_content
5880                            .terminal
5881                            .as_ref()
5882                            .and_then(|terminal| terminal.font_family.as_ref())
5883                            .or(settings_content.theme.buffer_font_family.as_ref())
5884                    },
5885                    write: |settings_content, value| {
5886                        settings_content
5887                            .terminal
5888                            .get_or_insert_default()
5889                            .font_family = value;
5890                    },
5891                }),
5892                metadata: None,
5893                files: USER,
5894            }),
5895            SettingsPageItem::SettingItem(SettingItem {
5896                title: "Font Fallbacks",
5897                description: "Font fallbacks for terminal text. If not set, defaults to buffer font fallbacks.",
5898                field: Box::new(
5899                    SettingField {
5900                        json_path: Some("terminal.font_fallbacks"),
5901                        pick: |settings_content| {
5902                            settings_content
5903                                .terminal
5904                                .as_ref()
5905                                .and_then(|terminal| terminal.font_fallbacks.as_ref())
5906                                .or(settings_content.theme.buffer_font_fallbacks.as_ref())
5907                        },
5908                        write: |settings_content, value| {
5909                            settings_content
5910                                .terminal
5911                                .get_or_insert_default()
5912                                .font_fallbacks = value;
5913                        },
5914                    }
5915                    .unimplemented(),
5916                ),
5917                metadata: None,
5918                files: USER,
5919            }),
5920            SettingsPageItem::SettingItem(SettingItem {
5921                title: "Font Weight",
5922                description: "Font weight for terminal text in CSS weight units (100-900).",
5923                field: Box::new(SettingField {
5924                    json_path: Some("terminal.font_weight"),
5925                    pick: |settings_content| {
5926                        settings_content.terminal.as_ref()?.font_weight.as_ref()
5927                    },
5928                    write: |settings_content, value| {
5929                        settings_content
5930                            .terminal
5931                            .get_or_insert_default()
5932                            .font_weight = value;
5933                    },
5934                }),
5935                metadata: None,
5936                files: USER,
5937            }),
5938            SettingsPageItem::SettingItem(SettingItem {
5939                title: "Font Features",
5940                description: "Font features for terminal text.",
5941                field: Box::new(
5942                    SettingField {
5943                        json_path: Some("terminal.font_features"),
5944                        pick: |settings_content| {
5945                            settings_content
5946                                .terminal
5947                                .as_ref()
5948                                .and_then(|terminal| terminal.font_features.as_ref())
5949                                .or(settings_content.theme.buffer_font_features.as_ref())
5950                        },
5951                        write: |settings_content, value| {
5952                            settings_content
5953                                .terminal
5954                                .get_or_insert_default()
5955                                .font_features = value;
5956                        },
5957                    }
5958                    .unimplemented(),
5959                ),
5960                metadata: None,
5961                files: USER,
5962            }),
5963        ]
5964    }
5965
5966    fn display_settings_section() -> [SettingsPageItem; 6] {
5967        [
5968            SettingsPageItem::SectionHeader("Display Settings"),
5969            SettingsPageItem::SettingItem(SettingItem {
5970                title: "Line Height",
5971                description: "Line height for terminal text.",
5972                field: Box::new(
5973                    SettingField {
5974                        json_path: Some("terminal.line_height"),
5975                        pick: |settings_content| {
5976                            settings_content.terminal.as_ref()?.line_height.as_ref()
5977                        },
5978                        write: |settings_content, value| {
5979                            settings_content
5980                                .terminal
5981                                .get_or_insert_default()
5982                                .line_height = value;
5983                        },
5984                    }
5985                    .unimplemented(),
5986                ),
5987                metadata: None,
5988                files: USER,
5989            }),
5990            SettingsPageItem::SettingItem(SettingItem {
5991                title: "Cursor Shape",
5992                description: "Default cursor shape for the terminal (bar, block, underline, or hollow).",
5993                field: Box::new(SettingField {
5994                    json_path: Some("terminal.cursor_shape"),
5995                    pick: |settings_content| {
5996                        settings_content.terminal.as_ref()?.cursor_shape.as_ref()
5997                    },
5998                    write: |settings_content, value| {
5999                        settings_content
6000                            .terminal
6001                            .get_or_insert_default()
6002                            .cursor_shape = value;
6003                    },
6004                }),
6005                metadata: None,
6006                files: USER,
6007            }),
6008            SettingsPageItem::SettingItem(SettingItem {
6009                title: "Cursor Blinking",
6010                description: "Sets the cursor blinking behavior in the terminal.",
6011                field: Box::new(SettingField {
6012                    json_path: Some("terminal.blinking"),
6013                    pick: |settings_content| settings_content.terminal.as_ref()?.blinking.as_ref(),
6014                    write: |settings_content, value| {
6015                        settings_content.terminal.get_or_insert_default().blinking = value;
6016                    },
6017                }),
6018                metadata: None,
6019                files: USER,
6020            }),
6021            SettingsPageItem::SettingItem(SettingItem {
6022                title: "Alternate Scroll",
6023                description: "Whether alternate scroll mode is active by default (converts mouse scroll to arrow keys in apps like Vim).",
6024                field: Box::new(SettingField {
6025                    json_path: Some("terminal.alternate_scroll"),
6026                    pick: |settings_content| {
6027                        settings_content
6028                            .terminal
6029                            .as_ref()?
6030                            .alternate_scroll
6031                            .as_ref()
6032                    },
6033                    write: |settings_content, value| {
6034                        settings_content
6035                            .terminal
6036                            .get_or_insert_default()
6037                            .alternate_scroll = value;
6038                    },
6039                }),
6040                metadata: None,
6041                files: USER,
6042            }),
6043            SettingsPageItem::SettingItem(SettingItem {
6044                title: "Minimum Contrast",
6045                description: "The minimum APCA perceptual contrast between foreground and background colors (0-106).",
6046                field: Box::new(SettingField {
6047                    json_path: Some("terminal.minimum_contrast"),
6048                    pick: |settings_content| {
6049                        settings_content
6050                            .terminal
6051                            .as_ref()?
6052                            .minimum_contrast
6053                            .as_ref()
6054                    },
6055                    write: |settings_content, value| {
6056                        settings_content
6057                            .terminal
6058                            .get_or_insert_default()
6059                            .minimum_contrast = value;
6060                    },
6061                }),
6062                metadata: None,
6063                files: USER,
6064            }),
6065        ]
6066    }
6067
6068    fn behavior_settings_section() -> [SettingsPageItem; 4] {
6069        [
6070            SettingsPageItem::SectionHeader("Behavior Settings"),
6071            SettingsPageItem::SettingItem(SettingItem {
6072                title: "Option As Meta",
6073                description: "Whether the option key behaves as the meta key.",
6074                field: Box::new(SettingField {
6075                    json_path: Some("terminal.option_as_meta"),
6076                    pick: |settings_content| {
6077                        settings_content.terminal.as_ref()?.option_as_meta.as_ref()
6078                    },
6079                    write: |settings_content, value| {
6080                        settings_content
6081                            .terminal
6082                            .get_or_insert_default()
6083                            .option_as_meta = value;
6084                    },
6085                }),
6086                metadata: None,
6087                files: USER,
6088            }),
6089            SettingsPageItem::SettingItem(SettingItem {
6090                title: "Copy On Select",
6091                description: "Whether selecting text in the terminal automatically copies to the system clipboard.",
6092                field: Box::new(SettingField {
6093                    json_path: Some("terminal.copy_on_select"),
6094                    pick: |settings_content| {
6095                        settings_content.terminal.as_ref()?.copy_on_select.as_ref()
6096                    },
6097                    write: |settings_content, value| {
6098                        settings_content
6099                            .terminal
6100                            .get_or_insert_default()
6101                            .copy_on_select = value;
6102                    },
6103                }),
6104                metadata: None,
6105                files: USER,
6106            }),
6107            SettingsPageItem::SettingItem(SettingItem {
6108                title: "Keep Selection On Copy",
6109                description: "Whether to keep the text selection after copying it to the clipboard.",
6110                field: Box::new(SettingField {
6111                    json_path: Some("terminal.keep_selection_on_copy"),
6112                    pick: |settings_content| {
6113                        settings_content
6114                            .terminal
6115                            .as_ref()?
6116                            .keep_selection_on_copy
6117                            .as_ref()
6118                    },
6119                    write: |settings_content, value| {
6120                        settings_content
6121                            .terminal
6122                            .get_or_insert_default()
6123                            .keep_selection_on_copy = value;
6124                    },
6125                }),
6126                metadata: None,
6127                files: USER,
6128            }),
6129        ]
6130    }
6131
6132    fn layout_settings_section() -> [SettingsPageItem; 3] {
6133        [
6134            SettingsPageItem::SectionHeader("Layout Settings"),
6135            SettingsPageItem::SettingItem(SettingItem {
6136                title: "Default Width",
6137                description: "Default width when the terminal is docked to the left or right (in pixels).",
6138                field: Box::new(SettingField {
6139                    json_path: Some("terminal.default_width"),
6140                    pick: |settings_content| {
6141                        settings_content.terminal.as_ref()?.default_width.as_ref()
6142                    },
6143                    write: |settings_content, value| {
6144                        settings_content
6145                            .terminal
6146                            .get_or_insert_default()
6147                            .default_width = value;
6148                    },
6149                }),
6150                metadata: None,
6151                files: USER,
6152            }),
6153            SettingsPageItem::SettingItem(SettingItem {
6154                title: "Default Height",
6155                description: "Default height when the terminal is docked to the bottom (in pixels).",
6156                field: Box::new(SettingField {
6157                    json_path: Some("terminal.default_height"),
6158                    pick: |settings_content| {
6159                        settings_content.terminal.as_ref()?.default_height.as_ref()
6160                    },
6161                    write: |settings_content, value| {
6162                        settings_content
6163                            .terminal
6164                            .get_or_insert_default()
6165                            .default_height = value;
6166                    },
6167                }),
6168                metadata: None,
6169                files: USER,
6170            }),
6171        ]
6172    }
6173
6174    fn advanced_settings_section() -> [SettingsPageItem; 3] {
6175        [
6176            SettingsPageItem::SectionHeader("Advanced Settings"),
6177            SettingsPageItem::SettingItem(SettingItem {
6178                title: "Max Scroll History Lines",
6179                description: "Maximum number of lines to keep in scrollback history (max: 100,000; 0 disables scrolling).",
6180                field: Box::new(SettingField {
6181                    json_path: Some("terminal.max_scroll_history_lines"),
6182                    pick: |settings_content| {
6183                        settings_content
6184                            .terminal
6185                            .as_ref()?
6186                            .max_scroll_history_lines
6187                            .as_ref()
6188                    },
6189                    write: |settings_content, value| {
6190                        settings_content
6191                            .terminal
6192                            .get_or_insert_default()
6193                            .max_scroll_history_lines = value;
6194                    },
6195                }),
6196                metadata: None,
6197                files: USER,
6198            }),
6199            SettingsPageItem::SettingItem(SettingItem {
6200                title: "Scroll Multiplier",
6201                description: "The multiplier for scrolling in the terminal with the mouse wheel",
6202                field: Box::new(SettingField {
6203                    json_path: Some("terminal.scroll_multiplier"),
6204                    pick: |settings_content| {
6205                        settings_content
6206                            .terminal
6207                            .as_ref()?
6208                            .scroll_multiplier
6209                            .as_ref()
6210                    },
6211                    write: |settings_content, value| {
6212                        settings_content
6213                            .terminal
6214                            .get_or_insert_default()
6215                            .scroll_multiplier = value;
6216                    },
6217                }),
6218                metadata: None,
6219                files: USER,
6220            }),
6221        ]
6222    }
6223
6224    fn toolbar_section() -> [SettingsPageItem; 2] {
6225        [
6226            SettingsPageItem::SectionHeader("Toolbar"),
6227            SettingsPageItem::SettingItem(SettingItem {
6228                title: "Breadcrumbs",
6229                description: "Display the terminal title in breadcrumbs inside the terminal pane.",
6230                field: Box::new(SettingField {
6231                    json_path: Some("terminal.toolbar.breadcrumbs"),
6232                    pick: |settings_content| {
6233                        settings_content
6234                            .terminal
6235                            .as_ref()?
6236                            .toolbar
6237                            .as_ref()?
6238                            .breadcrumbs
6239                            .as_ref()
6240                    },
6241                    write: |settings_content, value| {
6242                        settings_content
6243                            .terminal
6244                            .get_or_insert_default()
6245                            .toolbar
6246                            .get_or_insert_default()
6247                            .breadcrumbs = value;
6248                    },
6249                }),
6250                metadata: None,
6251                files: USER,
6252            }),
6253        ]
6254    }
6255
6256    fn scrollbar_section() -> [SettingsPageItem; 2] {
6257        [
6258            SettingsPageItem::SectionHeader("Scrollbar"),
6259            SettingsPageItem::SettingItem(SettingItem {
6260                title: "Show Scrollbar",
6261                description: "When to show the scrollbar in the terminal.",
6262                field: Box::new(SettingField {
6263                    json_path: Some("terminal.scrollbar.show"),
6264                    pick: |settings_content| {
6265                        show_scrollbar_or_editor(settings_content, |settings_content| {
6266                            settings_content
6267                                .terminal
6268                                .as_ref()?
6269                                .scrollbar
6270                                .as_ref()?
6271                                .show
6272                                .as_ref()
6273                        })
6274                    },
6275                    write: |settings_content, value| {
6276                        settings_content
6277                            .terminal
6278                            .get_or_insert_default()
6279                            .scrollbar
6280                            .get_or_insert_default()
6281                            .show = value;
6282                    },
6283                }),
6284                metadata: None,
6285                files: USER,
6286            }),
6287        ]
6288    }
6289
6290    SettingsPage {
6291        title: "Terminal",
6292        items: concat_sections![
6293            environment_section(),
6294            font_section(),
6295            display_settings_section(),
6296            behavior_settings_section(),
6297            layout_settings_section(),
6298            advanced_settings_section(),
6299            toolbar_section(),
6300            scrollbar_section(),
6301        ],
6302    }
6303}
6304
6305fn version_control_page() -> SettingsPage {
6306    fn git_integration_section() -> [SettingsPageItem; 2] {
6307        [
6308            SettingsPageItem::SectionHeader("Git Integration"),
6309            SettingsPageItem::DynamicItem(DynamicItem {
6310                discriminant: SettingItem {
6311                    files: USER,
6312                    title: "Disable Git Integration",
6313                    description: "Disable all Git integration features in Zed.",
6314                    field: Box::new(SettingField::<bool> {
6315                        json_path: Some("git.disable_git"),
6316                        pick: |settings_content| {
6317                            settings_content
6318                                .git
6319                                .as_ref()?
6320                                .enabled
6321                                .as_ref()?
6322                                .disable_git
6323                                .as_ref()
6324                        },
6325                        write: |settings_content, value| {
6326                            settings_content
6327                                .git
6328                                .get_or_insert_default()
6329                                .enabled
6330                                .get_or_insert_default()
6331                                .disable_git = value;
6332                        },
6333                    }),
6334                    metadata: None,
6335                },
6336                pick_discriminant: |settings_content| {
6337                    let disabled = settings_content
6338                        .git
6339                        .as_ref()?
6340                        .enabled
6341                        .as_ref()?
6342                        .disable_git
6343                        .unwrap_or(false);
6344                    Some(if disabled { 0 } else { 1 })
6345                },
6346                fields: vec![
6347                    vec![],
6348                    vec![
6349                        SettingItem {
6350                            files: USER,
6351                            title: "Enable Git Status",
6352                            description: "Show Git status information in the editor.",
6353                            field: Box::new(SettingField::<bool> {
6354                                json_path: Some("git.enable_status"),
6355                                pick: |settings_content| {
6356                                    settings_content
6357                                        .git
6358                                        .as_ref()?
6359                                        .enabled
6360                                        .as_ref()?
6361                                        .enable_status
6362                                        .as_ref()
6363                                },
6364                                write: |settings_content, value| {
6365                                    settings_content
6366                                        .git
6367                                        .get_or_insert_default()
6368                                        .enabled
6369                                        .get_or_insert_default()
6370                                        .enable_status = value;
6371                                },
6372                            }),
6373                            metadata: None,
6374                        },
6375                        SettingItem {
6376                            files: USER,
6377                            title: "Enable Git Diff",
6378                            description: "Show Git diff information in the editor.",
6379                            field: Box::new(SettingField::<bool> {
6380                                json_path: Some("git.enable_diff"),
6381                                pick: |settings_content| {
6382                                    settings_content
6383                                        .git
6384                                        .as_ref()?
6385                                        .enabled
6386                                        .as_ref()?
6387                                        .enable_diff
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_diff = value;
6397                                },
6398                            }),
6399                            metadata: None,
6400                        },
6401                    ],
6402                ],
6403            }),
6404        ]
6405    }
6406
6407    fn git_gutter_section() -> [SettingsPageItem; 3] {
6408        [
6409            SettingsPageItem::SectionHeader("Git Gutter"),
6410            SettingsPageItem::SettingItem(SettingItem {
6411                title: "Visibility",
6412                description: "Control whether Git status is shown in the editor's gutter.",
6413                field: Box::new(SettingField {
6414                    json_path: Some("git.git_gutter"),
6415                    pick: |settings_content| settings_content.git.as_ref()?.git_gutter.as_ref(),
6416                    write: |settings_content, value| {
6417                        settings_content.git.get_or_insert_default().git_gutter = value;
6418                    },
6419                }),
6420                metadata: None,
6421                files: USER,
6422            }),
6423            // todo(settings_ui): Figure out the right default for this value in default.json
6424            SettingsPageItem::SettingItem(SettingItem {
6425                title: "Debounce",
6426                description: "Debounce threshold in milliseconds after which changes are reflected in the Git gutter.",
6427                field: Box::new(SettingField {
6428                    json_path: Some("git.gutter_debounce"),
6429                    pick: |settings_content| {
6430                        settings_content.git.as_ref()?.gutter_debounce.as_ref()
6431                    },
6432                    write: |settings_content, value| {
6433                        settings_content.git.get_or_insert_default().gutter_debounce = value;
6434                    },
6435                }),
6436                metadata: None,
6437                files: USER,
6438            }),
6439        ]
6440    }
6441
6442    fn inline_git_blame_section() -> [SettingsPageItem; 6] {
6443        [
6444            SettingsPageItem::SectionHeader("Inline Git Blame"),
6445            SettingsPageItem::SettingItem(SettingItem {
6446                title: "Enabled",
6447                description: "Whether or not to show Git blame data inline in the currently focused line.",
6448                field: Box::new(SettingField {
6449                    json_path: Some("git.inline_blame.enabled"),
6450                    pick: |settings_content| {
6451                        settings_content
6452                            .git
6453                            .as_ref()?
6454                            .inline_blame
6455                            .as_ref()?
6456                            .enabled
6457                            .as_ref()
6458                    },
6459                    write: |settings_content, value| {
6460                        settings_content
6461                            .git
6462                            .get_or_insert_default()
6463                            .inline_blame
6464                            .get_or_insert_default()
6465                            .enabled = value;
6466                    },
6467                }),
6468                metadata: None,
6469                files: USER,
6470            }),
6471            SettingsPageItem::SettingItem(SettingItem {
6472                title: "Delay",
6473                description: "The delay after which the inline blame information is shown.",
6474                field: Box::new(SettingField {
6475                    json_path: Some("git.inline_blame.delay_ms"),
6476                    pick: |settings_content| {
6477                        settings_content
6478                            .git
6479                            .as_ref()?
6480                            .inline_blame
6481                            .as_ref()?
6482                            .delay_ms
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                            .delay_ms = value;
6492                    },
6493                }),
6494                metadata: None,
6495                files: USER,
6496            }),
6497            SettingsPageItem::SettingItem(SettingItem {
6498                title: "Padding",
6499                description: "Padding between the end of the source line and the start of the inline blame in columns.",
6500                field: Box::new(SettingField {
6501                    json_path: Some("git.inline_blame.padding"),
6502                    pick: |settings_content| {
6503                        settings_content
6504                            .git
6505                            .as_ref()?
6506                            .inline_blame
6507                            .as_ref()?
6508                            .padding
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                            .padding = value;
6518                    },
6519                }),
6520                metadata: None,
6521                files: USER,
6522            }),
6523            SettingsPageItem::SettingItem(SettingItem {
6524                title: "Minimum Column",
6525                description: "The minimum column number at which to show the inline blame information.",
6526                field: Box::new(SettingField {
6527                    json_path: Some("git.inline_blame.min_column"),
6528                    pick: |settings_content| {
6529                        settings_content
6530                            .git
6531                            .as_ref()?
6532                            .inline_blame
6533                            .as_ref()?
6534                            .min_column
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                            .min_column = value;
6544                    },
6545                }),
6546                metadata: None,
6547                files: USER,
6548            }),
6549            SettingsPageItem::SettingItem(SettingItem {
6550                title: "Show Commit Summary",
6551                description: "Show commit summary as part of the inline blame.",
6552                field: Box::new(SettingField {
6553                    json_path: Some("git.inline_blame.show_commit_summary"),
6554                    pick: |settings_content| {
6555                        settings_content
6556                            .git
6557                            .as_ref()?
6558                            .inline_blame
6559                            .as_ref()?
6560                            .show_commit_summary
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                            .show_commit_summary = value;
6570                    },
6571                }),
6572                metadata: None,
6573                files: USER,
6574            }),
6575        ]
6576    }
6577
6578    fn git_blame_view_section() -> [SettingsPageItem; 2] {
6579        [
6580            SettingsPageItem::SectionHeader("Git Blame View"),
6581            SettingsPageItem::SettingItem(SettingItem {
6582                title: "Show Avatar",
6583                description: "Show the avatar of the author of the commit.",
6584                field: Box::new(SettingField {
6585                    json_path: Some("git.blame.show_avatar"),
6586                    pick: |settings_content| {
6587                        settings_content
6588                            .git
6589                            .as_ref()?
6590                            .blame
6591                            .as_ref()?
6592                            .show_avatar
6593                            .as_ref()
6594                    },
6595                    write: |settings_content, value| {
6596                        settings_content
6597                            .git
6598                            .get_or_insert_default()
6599                            .blame
6600                            .get_or_insert_default()
6601                            .show_avatar = value;
6602                    },
6603                }),
6604                metadata: None,
6605                files: USER,
6606            }),
6607        ]
6608    }
6609
6610    fn branch_picker_section() -> [SettingsPageItem; 2] {
6611        [
6612            SettingsPageItem::SectionHeader("Branch Picker"),
6613            SettingsPageItem::SettingItem(SettingItem {
6614                title: "Show Author Name",
6615                description: "Show author name as part of the commit information in branch picker.",
6616                field: Box::new(SettingField {
6617                    json_path: Some("git.branch_picker.show_author_name"),
6618                    pick: |settings_content| {
6619                        settings_content
6620                            .git
6621                            .as_ref()?
6622                            .branch_picker
6623                            .as_ref()?
6624                            .show_author_name
6625                            .as_ref()
6626                    },
6627                    write: |settings_content, value| {
6628                        settings_content
6629                            .git
6630                            .get_or_insert_default()
6631                            .branch_picker
6632                            .get_or_insert_default()
6633                            .show_author_name = value;
6634                    },
6635                }),
6636                metadata: None,
6637                files: USER,
6638            }),
6639        ]
6640    }
6641
6642    fn git_hunks_section() -> [SettingsPageItem; 3] {
6643        [
6644            SettingsPageItem::SectionHeader("Git Hunks"),
6645            SettingsPageItem::SettingItem(SettingItem {
6646                title: "Hunk Style",
6647                description: "How Git hunks are displayed visually in the editor.",
6648                field: Box::new(SettingField {
6649                    json_path: Some("git.hunk_style"),
6650                    pick: |settings_content| settings_content.git.as_ref()?.hunk_style.as_ref(),
6651                    write: |settings_content, value| {
6652                        settings_content.git.get_or_insert_default().hunk_style = value;
6653                    },
6654                }),
6655                metadata: None,
6656                files: USER,
6657            }),
6658            SettingsPageItem::SettingItem(SettingItem {
6659                title: "Path Style",
6660                description: "Should the name or path be displayed first in the git view.",
6661                field: Box::new(SettingField {
6662                    json_path: Some("git.path_style"),
6663                    pick: |settings_content| settings_content.git.as_ref()?.path_style.as_ref(),
6664                    write: |settings_content, value| {
6665                        settings_content.git.get_or_insert_default().path_style = value;
6666                    },
6667                }),
6668                metadata: None,
6669                files: USER,
6670            }),
6671        ]
6672    }
6673
6674    SettingsPage {
6675        title: "Version Control",
6676        items: concat_sections![
6677            git_integration_section(),
6678            git_gutter_section(),
6679            inline_git_blame_section(),
6680            git_blame_view_section(),
6681            branch_picker_section(),
6682            git_hunks_section(),
6683        ],
6684    }
6685}
6686
6687fn collaboration_page() -> SettingsPage {
6688    fn calls_section() -> [SettingsPageItem; 3] {
6689        [
6690            SettingsPageItem::SectionHeader("Calls"),
6691            SettingsPageItem::SettingItem(SettingItem {
6692                title: "Mute On Join",
6693                description: "Whether the microphone should be muted when joining a channel or a call.",
6694                field: Box::new(SettingField {
6695                    json_path: Some("calls.mute_on_join"),
6696                    pick: |settings_content| settings_content.calls.as_ref()?.mute_on_join.as_ref(),
6697                    write: |settings_content, value| {
6698                        settings_content.calls.get_or_insert_default().mute_on_join = value;
6699                    },
6700                }),
6701                metadata: None,
6702                files: USER,
6703            }),
6704            SettingsPageItem::SettingItem(SettingItem {
6705                title: "Share On Join",
6706                description: "Whether your current project should be shared when joining an empty channel.",
6707                field: Box::new(SettingField {
6708                    json_path: Some("calls.share_on_join"),
6709                    pick: |settings_content| {
6710                        settings_content.calls.as_ref()?.share_on_join.as_ref()
6711                    },
6712                    write: |settings_content, value| {
6713                        settings_content.calls.get_or_insert_default().share_on_join = value;
6714                    },
6715                }),
6716                metadata: None,
6717                files: USER,
6718            }),
6719        ]
6720    }
6721
6722    fn experimental_section() -> [SettingsPageItem; 6] {
6723        [
6724            SettingsPageItem::SectionHeader("Experimental"),
6725            SettingsPageItem::SettingItem(SettingItem {
6726                title: "Rodio Audio",
6727                description: "Opt into the new audio system.",
6728                field: Box::new(SettingField {
6729                    json_path: Some("audio.experimental.rodio_audio"),
6730                    pick: |settings_content| settings_content.audio.as_ref()?.rodio_audio.as_ref(),
6731                    write: |settings_content, value| {
6732                        settings_content.audio.get_or_insert_default().rodio_audio = value;
6733                    },
6734                }),
6735                metadata: None,
6736                files: USER,
6737            }),
6738            SettingsPageItem::SettingItem(SettingItem {
6739                title: "Auto Microphone Volume",
6740                description: "Automatically adjust microphone volume (requires rodio audio).",
6741                field: Box::new(SettingField {
6742                    json_path: Some("audio.experimental.auto_microphone_volume"),
6743                    pick: |settings_content| {
6744                        settings_content
6745                            .audio
6746                            .as_ref()?
6747                            .auto_microphone_volume
6748                            .as_ref()
6749                    },
6750                    write: |settings_content, value| {
6751                        settings_content
6752                            .audio
6753                            .get_or_insert_default()
6754                            .auto_microphone_volume = value;
6755                    },
6756                }),
6757                metadata: None,
6758                files: USER,
6759            }),
6760            SettingsPageItem::SettingItem(SettingItem {
6761                title: "Auto Speaker Volume",
6762                description: "Automatically adjust volume of other call members (requires rodio audio).",
6763                field: Box::new(SettingField {
6764                    json_path: Some("audio.experimental.auto_speaker_volume"),
6765                    pick: |settings_content| {
6766                        settings_content
6767                            .audio
6768                            .as_ref()?
6769                            .auto_speaker_volume
6770                            .as_ref()
6771                    },
6772                    write: |settings_content, value| {
6773                        settings_content
6774                            .audio
6775                            .get_or_insert_default()
6776                            .auto_speaker_volume = value;
6777                    },
6778                }),
6779                metadata: None,
6780                files: USER,
6781            }),
6782            SettingsPageItem::SettingItem(SettingItem {
6783                title: "Denoise",
6784                description: "Remove background noises (requires rodio audio).",
6785                field: Box::new(SettingField {
6786                    json_path: Some("audio.experimental.denoise"),
6787                    pick: |settings_content| settings_content.audio.as_ref()?.denoise.as_ref(),
6788                    write: |settings_content, value| {
6789                        settings_content.audio.get_or_insert_default().denoise = value;
6790                    },
6791                }),
6792                metadata: None,
6793                files: USER,
6794            }),
6795            SettingsPageItem::SettingItem(SettingItem {
6796                title: "Legacy Audio Compatible",
6797                description: "Use audio parameters compatible with previous versions (requires rodio audio).",
6798                field: Box::new(SettingField {
6799                    json_path: Some("audio.experimental.legacy_audio_compatible"),
6800                    pick: |settings_content| {
6801                        settings_content
6802                            .audio
6803                            .as_ref()?
6804                            .legacy_audio_compatible
6805                            .as_ref()
6806                    },
6807                    write: |settings_content, value| {
6808                        settings_content
6809                            .audio
6810                            .get_or_insert_default()
6811                            .legacy_audio_compatible = value;
6812                    },
6813                }),
6814                metadata: None,
6815                files: USER,
6816            }),
6817        ]
6818    }
6819
6820    SettingsPage {
6821        title: "Collaboration",
6822        items: concat_sections![calls_section(), experimental_section()],
6823    }
6824}
6825
6826fn ai_page() -> SettingsPage {
6827    fn general_section() -> [SettingsPageItem; 2] {
6828        [
6829            SettingsPageItem::SectionHeader("General"),
6830            SettingsPageItem::SettingItem(SettingItem {
6831                title: "Disable AI",
6832                description: "Whether to disable all AI features in Zed.",
6833                field: Box::new(SettingField {
6834                    json_path: Some("disable_ai"),
6835                    pick: |settings_content| settings_content.disable_ai.as_ref(),
6836                    write: |settings_content, value| {
6837                        settings_content.disable_ai = value;
6838                    },
6839                }),
6840                metadata: None,
6841                files: USER,
6842            }),
6843        ]
6844    }
6845
6846    fn agent_configuration_section() -> [SettingsPageItem; 12] {
6847        [
6848            SettingsPageItem::SectionHeader("Agent Configuration"),
6849            SettingsPageItem::SettingItem(SettingItem {
6850                title: "Always Allow Tool Actions",
6851                description: "When enabled, the agent can run potentially destructive actions without asking for your confirmation. This setting has no effect on external agents.",
6852                field: Box::new(SettingField {
6853                    json_path: Some("agent.always_allow_tool_actions"),
6854                    pick: |settings_content| {
6855                        settings_content
6856                            .agent
6857                            .as_ref()?
6858                            .always_allow_tool_actions
6859                            .as_ref()
6860                    },
6861                    write: |settings_content, value| {
6862                        settings_content
6863                            .agent
6864                            .get_or_insert_default()
6865                            .always_allow_tool_actions = value;
6866                    },
6867                }),
6868                metadata: None,
6869                files: USER,
6870            }),
6871            SettingsPageItem::SettingItem(SettingItem {
6872                title: "Single File Review",
6873                description: "When enabled, agent edits will also be displayed in single-file buffers for review.",
6874                field: Box::new(SettingField {
6875                    json_path: Some("agent.single_file_review"),
6876                    pick: |settings_content| {
6877                        settings_content.agent.as_ref()?.single_file_review.as_ref()
6878                    },
6879                    write: |settings_content, value| {
6880                        settings_content
6881                            .agent
6882                            .get_or_insert_default()
6883                            .single_file_review = value;
6884                    },
6885                }),
6886                metadata: None,
6887                files: USER,
6888            }),
6889            SettingsPageItem::SettingItem(SettingItem {
6890                title: "Enable Feedback",
6891                description: "Show voting thumbs up/down icon buttons for feedback on agent edits.",
6892                field: Box::new(SettingField {
6893                    json_path: Some("agent.enable_feedback"),
6894                    pick: |settings_content| {
6895                        settings_content.agent.as_ref()?.enable_feedback.as_ref()
6896                    },
6897                    write: |settings_content, value| {
6898                        settings_content
6899                            .agent
6900                            .get_or_insert_default()
6901                            .enable_feedback = value;
6902                    },
6903                }),
6904                metadata: None,
6905                files: USER,
6906            }),
6907            SettingsPageItem::SettingItem(SettingItem {
6908                title: "Notify When Agent Waiting",
6909                description: "Where to show notifications when the agent has completed its response or needs confirmation before running a tool action.",
6910                field: Box::new(SettingField {
6911                    json_path: Some("agent.notify_when_agent_waiting"),
6912                    pick: |settings_content| {
6913                        settings_content
6914                            .agent
6915                            .as_ref()?
6916                            .notify_when_agent_waiting
6917                            .as_ref()
6918                    },
6919                    write: |settings_content, value| {
6920                        settings_content
6921                            .agent
6922                            .get_or_insert_default()
6923                            .notify_when_agent_waiting = value;
6924                    },
6925                }),
6926                metadata: None,
6927                files: USER,
6928            }),
6929            SettingsPageItem::SettingItem(SettingItem {
6930                title: "Play Sound When Agent Done",
6931                description: "Whether to play a sound when the agent has either completed its response, or needs user input.",
6932                field: Box::new(SettingField {
6933                    json_path: Some("agent.play_sound_when_agent_done"),
6934                    pick: |settings_content| {
6935                        settings_content
6936                            .agent
6937                            .as_ref()?
6938                            .play_sound_when_agent_done
6939                            .as_ref()
6940                    },
6941                    write: |settings_content, value| {
6942                        settings_content
6943                            .agent
6944                            .get_or_insert_default()
6945                            .play_sound_when_agent_done = value;
6946                    },
6947                }),
6948                metadata: None,
6949                files: USER,
6950            }),
6951            SettingsPageItem::SettingItem(SettingItem {
6952                title: "Expand Edit Card",
6953                description: "Whether to have edit cards in the agent panel expanded, showing a Preview of the diff.",
6954                field: Box::new(SettingField {
6955                    json_path: Some("agent.expand_edit_card"),
6956                    pick: |settings_content| {
6957                        settings_content.agent.as_ref()?.expand_edit_card.as_ref()
6958                    },
6959                    write: |settings_content, value| {
6960                        settings_content
6961                            .agent
6962                            .get_or_insert_default()
6963                            .expand_edit_card = value;
6964                    },
6965                }),
6966                metadata: None,
6967                files: USER,
6968            }),
6969            SettingsPageItem::SettingItem(SettingItem {
6970                title: "Expand Terminal Card",
6971                description: "Whether to have terminal cards in the agent panel expanded, showing the whole command output.",
6972                field: Box::new(SettingField {
6973                    json_path: Some("agent.expand_terminal_card"),
6974                    pick: |settings_content| {
6975                        settings_content
6976                            .agent
6977                            .as_ref()?
6978                            .expand_terminal_card
6979                            .as_ref()
6980                    },
6981                    write: |settings_content, value| {
6982                        settings_content
6983                            .agent
6984                            .get_or_insert_default()
6985                            .expand_terminal_card = value;
6986                    },
6987                }),
6988                metadata: None,
6989                files: USER,
6990            }),
6991            SettingsPageItem::SettingItem(SettingItem {
6992                title: "Cancel Generation On Terminal Stop",
6993                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.",
6994                field: Box::new(SettingField {
6995                    json_path: Some("agent.cancel_generation_on_terminal_stop"),
6996                    pick: |settings_content| {
6997                        settings_content
6998                            .agent
6999                            .as_ref()?
7000                            .cancel_generation_on_terminal_stop
7001                            .as_ref()
7002                    },
7003                    write: |settings_content, value| {
7004                        settings_content
7005                            .agent
7006                            .get_or_insert_default()
7007                            .cancel_generation_on_terminal_stop = value;
7008                    },
7009                }),
7010                metadata: None,
7011                files: USER,
7012            }),
7013            SettingsPageItem::SettingItem(SettingItem {
7014                title: "Use Modifier To Send",
7015                description: "Whether to always use cmd-enter (or ctrl-enter on Linux or Windows) to send messages.",
7016                field: Box::new(SettingField {
7017                    json_path: Some("agent.use_modifier_to_send"),
7018                    pick: |settings_content| {
7019                        settings_content
7020                            .agent
7021                            .as_ref()?
7022                            .use_modifier_to_send
7023                            .as_ref()
7024                    },
7025                    write: |settings_content, value| {
7026                        settings_content
7027                            .agent
7028                            .get_or_insert_default()
7029                            .use_modifier_to_send = value;
7030                    },
7031                }),
7032                metadata: None,
7033                files: USER,
7034            }),
7035            SettingsPageItem::SettingItem(SettingItem {
7036                title: "Message Editor Min Lines",
7037                description: "Minimum number of lines to display in the agent message editor.",
7038                field: Box::new(SettingField {
7039                    json_path: Some("agent.message_editor_min_lines"),
7040                    pick: |settings_content| {
7041                        settings_content
7042                            .agent
7043                            .as_ref()?
7044                            .message_editor_min_lines
7045                            .as_ref()
7046                    },
7047                    write: |settings_content, value| {
7048                        settings_content
7049                            .agent
7050                            .get_or_insert_default()
7051                            .message_editor_min_lines = value;
7052                    },
7053                }),
7054                metadata: None,
7055                files: USER,
7056            }),
7057            SettingsPageItem::SettingItem(SettingItem {
7058                title: "Show Turn Stats",
7059                description: "Whether to show turn statistics like elapsed time during generation and final turn duration.",
7060                field: Box::new(SettingField {
7061                    json_path: Some("agent.show_turn_stats"),
7062                    pick: |settings_content| {
7063                        settings_content.agent.as_ref()?.show_turn_stats.as_ref()
7064                    },
7065                    write: |settings_content, value| {
7066                        settings_content
7067                            .agent
7068                            .get_or_insert_default()
7069                            .show_turn_stats = value;
7070                    },
7071                }),
7072                metadata: None,
7073                files: USER,
7074            }),
7075        ]
7076    }
7077
7078    fn context_servers_section() -> [SettingsPageItem; 2] {
7079        [
7080            SettingsPageItem::SectionHeader("Context Servers"),
7081            SettingsPageItem::SettingItem(SettingItem {
7082                title: "Context Server Timeout",
7083                description: "Default timeout in seconds for context server tool calls. Can be overridden per-server in context_servers configuration.",
7084                field: Box::new(SettingField {
7085                    json_path: Some("context_server_timeout"),
7086                    pick: |settings_content| {
7087                        settings_content.project.context_server_timeout.as_ref()
7088                    },
7089                    write: |settings_content, value| {
7090                        settings_content.project.context_server_timeout = value;
7091                    },
7092                }),
7093                metadata: None,
7094                files: USER | PROJECT,
7095            }),
7096        ]
7097    }
7098
7099    fn edit_prediction_display_sub_section() -> [SettingsPageItem; 2] {
7100        [
7101            SettingsPageItem::SettingItem(SettingItem {
7102                title: "Display Mode",
7103                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.",
7104                field: Box::new(SettingField {
7105                    json_path: Some("edit_prediction.display_mode"),
7106                    pick: |settings_content| {
7107                        settings_content
7108                            .project
7109                            .all_languages
7110                            .edit_predictions
7111                            .as_ref()?
7112                            .mode
7113                            .as_ref()
7114                    },
7115                    write: |settings_content, value| {
7116                        settings_content
7117                            .project
7118                            .all_languages
7119                            .edit_predictions
7120                            .get_or_insert_default()
7121                            .mode = value;
7122                    },
7123                }),
7124                metadata: None,
7125                files: USER,
7126            }),
7127            SettingsPageItem::SettingItem(SettingItem {
7128                title: "Display In Text Threads",
7129                description: "Whether edit predictions are enabled when editing text threads in the agent panel.",
7130                field: Box::new(SettingField {
7131                    json_path: Some("edit_prediction.in_text_threads"),
7132                    pick: |settings_content| {
7133                        settings_content
7134                            .project
7135                            .all_languages
7136                            .edit_predictions
7137                            .as_ref()?
7138                            .enabled_in_text_threads
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                            .enabled_in_text_threads = value;
7148                    },
7149                }),
7150                metadata: None,
7151                files: USER,
7152            }),
7153        ]
7154    }
7155
7156    SettingsPage {
7157        title: "AI",
7158        items: concat_sections![
7159            general_section(),
7160            agent_configuration_section(),
7161            context_servers_section(),
7162            edit_prediction_language_settings_section(),
7163            edit_prediction_display_sub_section()
7164        ],
7165    }
7166}
7167
7168fn network_page() -> SettingsPage {
7169    fn network_section() -> [SettingsPageItem; 3] {
7170        [
7171            SettingsPageItem::SectionHeader("Network"),
7172            SettingsPageItem::SettingItem(SettingItem {
7173                title: "Proxy",
7174                description: "The proxy to use for network requests.",
7175                field: Box::new(SettingField {
7176                    json_path: Some("proxy"),
7177                    pick: |settings_content| settings_content.proxy.as_ref(),
7178                    write: |settings_content, value| {
7179                        settings_content.proxy = value;
7180                    },
7181                }),
7182                metadata: Some(Box::new(SettingsFieldMetadata {
7183                    placeholder: Some("socks5h://localhost:10808"),
7184                    ..Default::default()
7185                })),
7186                files: USER,
7187            }),
7188            SettingsPageItem::SettingItem(SettingItem {
7189                title: "Server URL",
7190                description: "The URL of the Zed server to connect to.",
7191                field: Box::new(SettingField {
7192                    json_path: Some("server_url"),
7193                    pick: |settings_content| settings_content.server_url.as_ref(),
7194                    write: |settings_content, value| {
7195                        settings_content.server_url = value;
7196                    },
7197                }),
7198                metadata: Some(Box::new(SettingsFieldMetadata {
7199                    placeholder: Some("https://zed.dev"),
7200                    ..Default::default()
7201                })),
7202                files: USER,
7203            }),
7204        ]
7205    }
7206
7207    SettingsPage {
7208        title: "Network",
7209        items: concat_sections![network_section()],
7210    }
7211}
7212
7213fn language_settings_field<T>(
7214    settings_content: &SettingsContent,
7215    get_language_setting_field: fn(&LanguageSettingsContent) -> Option<&T>,
7216) -> Option<&T> {
7217    let all_languages = &settings_content.project.all_languages;
7218
7219    active_language()
7220        .and_then(|current_language_name| {
7221            all_languages
7222                .languages
7223                .0
7224                .get(current_language_name.as_ref())
7225        })
7226        .and_then(get_language_setting_field)
7227        .or_else(|| get_language_setting_field(&all_languages.defaults))
7228}
7229
7230fn language_settings_field_mut<T>(
7231    settings_content: &mut SettingsContent,
7232    value: Option<T>,
7233    write: fn(&mut LanguageSettingsContent, Option<T>),
7234) {
7235    let all_languages = &mut settings_content.project.all_languages;
7236    let language_content = if let Some(current_language) = active_language() {
7237        all_languages
7238            .languages
7239            .0
7240            .entry(current_language.to_string())
7241            .or_default()
7242    } else {
7243        &mut all_languages.defaults
7244    };
7245    write(language_content, value);
7246}
7247
7248fn language_settings_data() -> Box<[SettingsPageItem]> {
7249    fn indentation_section() -> [SettingsPageItem; 5] {
7250        [
7251            SettingsPageItem::SectionHeader("Indentation"),
7252            SettingsPageItem::SettingItem(SettingItem {
7253                title: "Tab Size",
7254                description: "How many columns a tab should occupy.",
7255                field: Box::new(SettingField {
7256                    json_path: Some("languages.$(language).tab_size"), // TODO(cameron): not JQ syntax because not URL-safe
7257                    pick: |settings_content| {
7258                        language_settings_field(settings_content, |language| {
7259                            language.tab_size.as_ref()
7260                        })
7261                    },
7262                    write: |settings_content, value| {
7263                        language_settings_field_mut(settings_content, value, |language, value| {
7264                            language.tab_size = value;
7265                        })
7266                    },
7267                }),
7268                metadata: None,
7269                files: USER | PROJECT,
7270            }),
7271            SettingsPageItem::SettingItem(SettingItem {
7272                title: "Hard Tabs",
7273                description: "Whether to indent lines using tab characters, as opposed to multiple spaces.",
7274                field: Box::new(SettingField {
7275                    json_path: Some("languages.$(language).hard_tabs"),
7276                    pick: |settings_content| {
7277                        language_settings_field(settings_content, |language| {
7278                            language.hard_tabs.as_ref()
7279                        })
7280                    },
7281                    write: |settings_content, value| {
7282                        language_settings_field_mut(settings_content, value, |language, value| {
7283                            language.hard_tabs = value;
7284                        })
7285                    },
7286                }),
7287                metadata: None,
7288                files: USER | PROJECT,
7289            }),
7290            SettingsPageItem::SettingItem(SettingItem {
7291                title: "Auto Indent",
7292                description: "Whether indentation should be adjusted based on the context whilst typing.",
7293                field: Box::new(SettingField {
7294                    json_path: Some("languages.$(language).auto_indent"),
7295                    pick: |settings_content| {
7296                        language_settings_field(settings_content, |language| {
7297                            language.auto_indent.as_ref()
7298                        })
7299                    },
7300                    write: |settings_content, value| {
7301                        language_settings_field_mut(settings_content, value, |language, value| {
7302                            language.auto_indent = value;
7303                        })
7304                    },
7305                }),
7306                metadata: None,
7307                files: USER | PROJECT,
7308            }),
7309            SettingsPageItem::SettingItem(SettingItem {
7310                title: "Auto Indent On Paste",
7311                description: "Whether indentation of pasted content should be adjusted based on the context.",
7312                field: Box::new(SettingField {
7313                    json_path: Some("languages.$(language).auto_indent_on_paste"),
7314                    pick: |settings_content| {
7315                        language_settings_field(settings_content, |language| {
7316                            language.auto_indent_on_paste.as_ref()
7317                        })
7318                    },
7319                    write: |settings_content, value| {
7320                        language_settings_field_mut(settings_content, value, |language, value| {
7321                            language.auto_indent_on_paste = value;
7322                        })
7323                    },
7324                }),
7325                metadata: None,
7326                files: USER | PROJECT,
7327            }),
7328        ]
7329    }
7330
7331    fn wrapping_section() -> [SettingsPageItem; 6] {
7332        [
7333            SettingsPageItem::SectionHeader("Wrapping"),
7334            SettingsPageItem::SettingItem(SettingItem {
7335                title: "Soft Wrap",
7336                description: "How to soft-wrap long lines of text.",
7337                field: Box::new(SettingField {
7338                    json_path: Some("languages.$(language).soft_wrap"),
7339                    pick: |settings_content| {
7340                        language_settings_field(settings_content, |language| {
7341                            language.soft_wrap.as_ref()
7342                        })
7343                    },
7344                    write: |settings_content, value| {
7345                        language_settings_field_mut(settings_content, value, |language, value| {
7346                            language.soft_wrap = value;
7347                        })
7348                    },
7349                }),
7350                metadata: None,
7351                files: USER | PROJECT,
7352            }),
7353            SettingsPageItem::SettingItem(SettingItem {
7354                title: "Show Wrap Guides",
7355                description: "Show wrap guides in the editor.",
7356                field: Box::new(SettingField {
7357                    json_path: Some("languages.$(language).show_wrap_guides"),
7358                    pick: |settings_content| {
7359                        language_settings_field(settings_content, |language| {
7360                            language.show_wrap_guides.as_ref()
7361                        })
7362                    },
7363                    write: |settings_content, value| {
7364                        language_settings_field_mut(settings_content, value, |language, value| {
7365                            language.show_wrap_guides = value;
7366                        })
7367                    },
7368                }),
7369                metadata: None,
7370                files: USER | PROJECT,
7371            }),
7372            SettingsPageItem::SettingItem(SettingItem {
7373                title: "Preferred Line Length",
7374                description: "The column at which to soft-wrap lines, for buffers where soft-wrap is enabled.",
7375                field: Box::new(SettingField {
7376                    json_path: Some("languages.$(language).preferred_line_length"),
7377                    pick: |settings_content| {
7378                        language_settings_field(settings_content, |language| {
7379                            language.preferred_line_length.as_ref()
7380                        })
7381                    },
7382                    write: |settings_content, value| {
7383                        language_settings_field_mut(settings_content, value, |language, value| {
7384                            language.preferred_line_length = value;
7385                        })
7386                    },
7387                }),
7388                metadata: None,
7389                files: USER | PROJECT,
7390            }),
7391            SettingsPageItem::SettingItem(SettingItem {
7392                title: "Wrap Guides",
7393                description: "Character counts at which to show wrap guides in the editor.",
7394                field: Box::new(
7395                    SettingField {
7396                        json_path: Some("languages.$(language).wrap_guides"),
7397                        pick: |settings_content| {
7398                            language_settings_field(settings_content, |language| {
7399                                language.wrap_guides.as_ref()
7400                            })
7401                        },
7402                        write: |settings_content, value| {
7403                            language_settings_field_mut(
7404                                settings_content,
7405                                value,
7406                                |language, value| {
7407                                    language.wrap_guides = value;
7408                                },
7409                            )
7410                        },
7411                    }
7412                    .unimplemented(),
7413                ),
7414                metadata: None,
7415                files: USER | PROJECT,
7416            }),
7417            SettingsPageItem::SettingItem(SettingItem {
7418                title: "Allow Rewrap",
7419                description: "Controls where the `editor::rewrap` action is allowed for this language.",
7420                field: Box::new(SettingField {
7421                    json_path: Some("languages.$(language).allow_rewrap"),
7422                    pick: |settings_content| {
7423                        language_settings_field(settings_content, |language| {
7424                            language.allow_rewrap.as_ref()
7425                        })
7426                    },
7427                    write: |settings_content, value| {
7428                        language_settings_field_mut(settings_content, value, |language, value| {
7429                            language.allow_rewrap = value;
7430                        })
7431                    },
7432                }),
7433                metadata: None,
7434                files: USER | PROJECT,
7435            }),
7436        ]
7437    }
7438
7439    fn indent_guides_section() -> [SettingsPageItem; 6] {
7440        [
7441            SettingsPageItem::SectionHeader("Indent Guides"),
7442            SettingsPageItem::SettingItem(SettingItem {
7443                title: "Enabled",
7444                description: "Display indent guides in the editor.",
7445                field: Box::new(SettingField {
7446                    json_path: Some("languages.$(language).indent_guides.enabled"),
7447                    pick: |settings_content| {
7448                        language_settings_field(settings_content, |language| {
7449                            language
7450                                .indent_guides
7451                                .as_ref()
7452                                .and_then(|indent_guides| indent_guides.enabled.as_ref())
7453                        })
7454                    },
7455                    write: |settings_content, value| {
7456                        language_settings_field_mut(settings_content, value, |language, value| {
7457                            language.indent_guides.get_or_insert_default().enabled = value;
7458                        })
7459                    },
7460                }),
7461                metadata: None,
7462                files: USER | PROJECT,
7463            }),
7464            SettingsPageItem::SettingItem(SettingItem {
7465                title: "Line Width",
7466                description: "The width of the indent guides in pixels, between 1 and 10.",
7467                field: Box::new(SettingField {
7468                    json_path: Some("languages.$(language).indent_guides.line_width"),
7469                    pick: |settings_content| {
7470                        language_settings_field(settings_content, |language| {
7471                            language
7472                                .indent_guides
7473                                .as_ref()
7474                                .and_then(|indent_guides| indent_guides.line_width.as_ref())
7475                        })
7476                    },
7477                    write: |settings_content, value| {
7478                        language_settings_field_mut(settings_content, value, |language, value| {
7479                            language.indent_guides.get_or_insert_default().line_width = value;
7480                        })
7481                    },
7482                }),
7483                metadata: None,
7484                files: USER | PROJECT,
7485            }),
7486            SettingsPageItem::SettingItem(SettingItem {
7487                title: "Active Line Width",
7488                description: "The width of the active indent guide in pixels, between 1 and 10.",
7489                field: Box::new(SettingField {
7490                    json_path: Some("languages.$(language).indent_guides.active_line_width"),
7491                    pick: |settings_content| {
7492                        language_settings_field(settings_content, |language| {
7493                            language
7494                                .indent_guides
7495                                .as_ref()
7496                                .and_then(|indent_guides| indent_guides.active_line_width.as_ref())
7497                        })
7498                    },
7499                    write: |settings_content, value| {
7500                        language_settings_field_mut(settings_content, value, |language, value| {
7501                            language
7502                                .indent_guides
7503                                .get_or_insert_default()
7504                                .active_line_width = value;
7505                        })
7506                    },
7507                }),
7508                metadata: None,
7509                files: USER | PROJECT,
7510            }),
7511            SettingsPageItem::SettingItem(SettingItem {
7512                title: "Coloring",
7513                description: "Determines how indent guides are colored.",
7514                field: Box::new(SettingField {
7515                    json_path: Some("languages.$(language).indent_guides.coloring"),
7516                    pick: |settings_content| {
7517                        language_settings_field(settings_content, |language| {
7518                            language
7519                                .indent_guides
7520                                .as_ref()
7521                                .and_then(|indent_guides| indent_guides.coloring.as_ref())
7522                        })
7523                    },
7524                    write: |settings_content, value| {
7525                        language_settings_field_mut(settings_content, value, |language, value| {
7526                            language.indent_guides.get_or_insert_default().coloring = value;
7527                        })
7528                    },
7529                }),
7530                metadata: None,
7531                files: USER | PROJECT,
7532            }),
7533            SettingsPageItem::SettingItem(SettingItem {
7534                title: "Background Coloring",
7535                description: "Determines how indent guide backgrounds are colored.",
7536                field: Box::new(SettingField {
7537                    json_path: Some("languages.$(language).indent_guides.background_coloring"),
7538                    pick: |settings_content| {
7539                        language_settings_field(settings_content, |language| {
7540                            language.indent_guides.as_ref().and_then(|indent_guides| {
7541                                indent_guides.background_coloring.as_ref()
7542                            })
7543                        })
7544                    },
7545                    write: |settings_content, value| {
7546                        language_settings_field_mut(settings_content, value, |language, value| {
7547                            language
7548                                .indent_guides
7549                                .get_or_insert_default()
7550                                .background_coloring = value;
7551                        })
7552                    },
7553                }),
7554                metadata: None,
7555                files: USER | PROJECT,
7556            }),
7557        ]
7558    }
7559
7560    fn formatting_section() -> [SettingsPageItem; 7] {
7561        [
7562            SettingsPageItem::SectionHeader("Formatting"),
7563            SettingsPageItem::SettingItem(SettingItem {
7564                title: "Format On Save",
7565                description: "Whether or not to perform a buffer format before saving.",
7566                field: Box::new(
7567                    // TODO(settings_ui): this setting should just be a bool
7568                    SettingField {
7569                        json_path: Some("languages.$(language).format_on_save"),
7570                        pick: |settings_content| {
7571                            language_settings_field(settings_content, |language| {
7572                                language.format_on_save.as_ref()
7573                            })
7574                        },
7575                        write: |settings_content, value| {
7576                            language_settings_field_mut(
7577                                settings_content,
7578                                value,
7579                                |language, value| {
7580                                    language.format_on_save = value;
7581                                },
7582                            )
7583                        },
7584                    },
7585                ),
7586                metadata: None,
7587                files: USER | PROJECT,
7588            }),
7589            SettingsPageItem::SettingItem(SettingItem {
7590                title: "Remove Trailing Whitespace On Save",
7591                description: "Whether or not to remove any trailing whitespace from lines of a buffer before saving it.",
7592                field: Box::new(SettingField {
7593                    json_path: Some("languages.$(language).remove_trailing_whitespace_on_save"),
7594                    pick: |settings_content| {
7595                        language_settings_field(settings_content, |language| {
7596                            language.remove_trailing_whitespace_on_save.as_ref()
7597                        })
7598                    },
7599                    write: |settings_content, value| {
7600                        language_settings_field_mut(settings_content, value, |language, value| {
7601                            language.remove_trailing_whitespace_on_save = value;
7602                        })
7603                    },
7604                }),
7605                metadata: None,
7606                files: USER | PROJECT,
7607            }),
7608            SettingsPageItem::SettingItem(SettingItem {
7609                title: "Ensure Final Newline On Save",
7610                description: "Whether or not to ensure there's a single newline at the end of a buffer when saving it.",
7611                field: Box::new(SettingField {
7612                    json_path: Some("languages.$(language).ensure_final_newline_on_save"),
7613                    pick: |settings_content| {
7614                        language_settings_field(settings_content, |language| {
7615                            language.ensure_final_newline_on_save.as_ref()
7616                        })
7617                    },
7618                    write: |settings_content, value| {
7619                        language_settings_field_mut(settings_content, value, |language, value| {
7620                            language.ensure_final_newline_on_save = value;
7621                        })
7622                    },
7623                }),
7624                metadata: None,
7625                files: USER | PROJECT,
7626            }),
7627            SettingsPageItem::SettingItem(SettingItem {
7628                title: "Formatter",
7629                description: "How to perform a buffer format.",
7630                field: Box::new(
7631                    SettingField {
7632                        json_path: Some("languages.$(language).formatter"),
7633                        pick: |settings_content| {
7634                            language_settings_field(settings_content, |language| {
7635                                language.formatter.as_ref()
7636                            })
7637                        },
7638                        write: |settings_content, value| {
7639                            language_settings_field_mut(
7640                                settings_content,
7641                                value,
7642                                |language, value| {
7643                                    language.formatter = value;
7644                                },
7645                            )
7646                        },
7647                    }
7648                    .unimplemented(),
7649                ),
7650                metadata: None,
7651                files: USER | PROJECT,
7652            }),
7653            SettingsPageItem::SettingItem(SettingItem {
7654                title: "Use On Type Format",
7655                description: "Whether to use additional LSP queries to format (and amend) the code after every \"trigger\" symbol input, defined by LSP server capabilities",
7656                field: Box::new(SettingField {
7657                    json_path: Some("languages.$(language).use_on_type_format"),
7658                    pick: |settings_content| {
7659                        language_settings_field(settings_content, |language| {
7660                            language.use_on_type_format.as_ref()
7661                        })
7662                    },
7663                    write: |settings_content, value| {
7664                        language_settings_field_mut(settings_content, value, |language, value| {
7665                            language.use_on_type_format = value;
7666                        })
7667                    },
7668                }),
7669                metadata: None,
7670                files: USER | PROJECT,
7671            }),
7672            SettingsPageItem::SettingItem(SettingItem {
7673                title: "Code Actions On Format",
7674                description: "Additional code actions to run when formatting.",
7675                field: Box::new(
7676                    SettingField {
7677                        json_path: Some("languages.$(language).code_actions_on_format"),
7678                        pick: |settings_content| {
7679                            language_settings_field(settings_content, |language| {
7680                                language.code_actions_on_format.as_ref()
7681                            })
7682                        },
7683                        write: |settings_content, value| {
7684                            language_settings_field_mut(
7685                                settings_content,
7686                                value,
7687                                |language, value| {
7688                                    language.code_actions_on_format = value;
7689                                },
7690                            )
7691                        },
7692                    }
7693                    .unimplemented(),
7694                ),
7695                metadata: None,
7696                files: USER | PROJECT,
7697            }),
7698        ]
7699    }
7700
7701    fn autoclose_section() -> [SettingsPageItem; 5] {
7702        [
7703            SettingsPageItem::SectionHeader("Autoclose"),
7704            SettingsPageItem::SettingItem(SettingItem {
7705                title: "Use Autoclose",
7706                description: "Whether to automatically type closing characters for you. For example, when you type '(', Zed will automatically add a closing ')' at the correct position.",
7707                field: Box::new(SettingField {
7708                    json_path: Some("languages.$(language).use_autoclose"),
7709                    pick: |settings_content| {
7710                        language_settings_field(settings_content, |language| {
7711                            language.use_autoclose.as_ref()
7712                        })
7713                    },
7714                    write: |settings_content, value| {
7715                        language_settings_field_mut(settings_content, value, |language, value| {
7716                            language.use_autoclose = value;
7717                        })
7718                    },
7719                }),
7720                metadata: None,
7721                files: USER | PROJECT,
7722            }),
7723            SettingsPageItem::SettingItem(SettingItem {
7724                title: "Use Auto Surround",
7725                description: "Whether to automatically surround text with characters for you. For example, when you select text and type '(', Zed will automatically surround text with ().",
7726                field: Box::new(SettingField {
7727                    json_path: Some("languages.$(language).use_auto_surround"),
7728                    pick: |settings_content| {
7729                        language_settings_field(settings_content, |language| {
7730                            language.use_auto_surround.as_ref()
7731                        })
7732                    },
7733                    write: |settings_content, value| {
7734                        language_settings_field_mut(settings_content, value, |language, value| {
7735                            language.use_auto_surround = value;
7736                        })
7737                    },
7738                }),
7739                metadata: None,
7740                files: USER | PROJECT,
7741            }),
7742            SettingsPageItem::SettingItem(SettingItem {
7743                title: "Always Treat Brackets As Autoclosed",
7744                description: "Controls whether the closing characters are always skipped over and auto-removed no matter how they were inserted.",
7745                field: Box::new(SettingField {
7746                    json_path: Some("languages.$(language).always_treat_brackets_as_autoclosed"),
7747                    pick: |settings_content| {
7748                        language_settings_field(settings_content, |language| {
7749                            language.always_treat_brackets_as_autoclosed.as_ref()
7750                        })
7751                    },
7752                    write: |settings_content, value| {
7753                        language_settings_field_mut(settings_content, value, |language, value| {
7754                            language.always_treat_brackets_as_autoclosed = value;
7755                        })
7756                    },
7757                }),
7758                metadata: None,
7759                files: USER | PROJECT,
7760            }),
7761            SettingsPageItem::SettingItem(SettingItem {
7762                title: "JSX Tag Auto Close",
7763                description: "Whether to automatically close JSX tags.",
7764                field: Box::new(SettingField {
7765                    json_path: Some("languages.$(language).jsx_tag_auto_close"),
7766                    // TODO(settings_ui): this setting should just be a bool
7767                    pick: |settings_content| {
7768                        language_settings_field(settings_content, |language| {
7769                            language.jsx_tag_auto_close.as_ref()?.enabled.as_ref()
7770                        })
7771                    },
7772                    write: |settings_content, value| {
7773                        language_settings_field_mut(settings_content, value, |language, value| {
7774                            language.jsx_tag_auto_close.get_or_insert_default().enabled = value;
7775                        })
7776                    },
7777                }),
7778                metadata: None,
7779                files: USER | PROJECT,
7780            }),
7781        ]
7782    }
7783
7784    fn whitespace_section() -> [SettingsPageItem; 4] {
7785        [
7786            SettingsPageItem::SectionHeader("Whitespace"),
7787            SettingsPageItem::SettingItem(SettingItem {
7788                title: "Show Whitespaces",
7789                description: "Whether to show tabs and spaces in the editor.",
7790                field: Box::new(SettingField {
7791                    json_path: Some("languages.$(language).show_whitespaces"),
7792                    pick: |settings_content| {
7793                        language_settings_field(settings_content, |language| {
7794                            language.show_whitespaces.as_ref()
7795                        })
7796                    },
7797                    write: |settings_content, value| {
7798                        language_settings_field_mut(settings_content, value, |language, value| {
7799                            language.show_whitespaces = value;
7800                        })
7801                    },
7802                }),
7803                metadata: None,
7804                files: USER | PROJECT,
7805            }),
7806            SettingsPageItem::SettingItem(SettingItem {
7807                title: "Space Whitespace Indicator",
7808                description: "Visible character used to render space characters when show_whitespaces is enabled (default: \"\")",
7809                field: Box::new(
7810                    SettingField {
7811                        json_path: Some("languages.$(language).whitespace_map.space"),
7812                        pick: |settings_content| {
7813                            language_settings_field(settings_content, |language| {
7814                                language.whitespace_map.as_ref()?.space.as_ref()
7815                            })
7816                        },
7817                        write: |settings_content, value| {
7818                            language_settings_field_mut(
7819                                settings_content,
7820                                value,
7821                                |language, value| {
7822                                    language.whitespace_map.get_or_insert_default().space = value;
7823                                },
7824                            )
7825                        },
7826                    }
7827                    .unimplemented(),
7828                ),
7829                metadata: None,
7830                files: USER | PROJECT,
7831            }),
7832            SettingsPageItem::SettingItem(SettingItem {
7833                title: "Tab Whitespace Indicator",
7834                description: "Visible character used to render tab characters when show_whitespaces is enabled (default: \"\")",
7835                field: Box::new(
7836                    SettingField {
7837                        json_path: Some("languages.$(language).whitespace_map.tab"),
7838                        pick: |settings_content| {
7839                            language_settings_field(settings_content, |language| {
7840                                language.whitespace_map.as_ref()?.tab.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().tab = value;
7849                                },
7850                            )
7851                        },
7852                    }
7853                    .unimplemented(),
7854                ),
7855                metadata: None,
7856                files: USER | PROJECT,
7857            }),
7858        ]
7859    }
7860
7861    fn completions_section() -> [SettingsPageItem; 7] {
7862        [
7863            SettingsPageItem::SectionHeader("Completions"),
7864            SettingsPageItem::SettingItem(SettingItem {
7865                title: "Show Completions On Input",
7866                description: "Whether to pop the completions menu while typing in an editor without explicitly requesting it.",
7867                field: Box::new(SettingField {
7868                    json_path: Some("languages.$(language).show_completions_on_input"),
7869                    pick: |settings_content| {
7870                        language_settings_field(settings_content, |language| {
7871                            language.show_completions_on_input.as_ref()
7872                        })
7873                    },
7874                    write: |settings_content, value| {
7875                        language_settings_field_mut(settings_content, value, |language, value| {
7876                            language.show_completions_on_input = value;
7877                        })
7878                    },
7879                }),
7880                metadata: None,
7881                files: USER | PROJECT,
7882            }),
7883            SettingsPageItem::SettingItem(SettingItem {
7884                title: "Show Completion Documentation",
7885                description: "Whether to display inline and alongside documentation for items in the completions menu.",
7886                field: Box::new(SettingField {
7887                    json_path: Some("languages.$(language).show_completion_documentation"),
7888                    pick: |settings_content| {
7889                        language_settings_field(settings_content, |language| {
7890                            language.show_completion_documentation.as_ref()
7891                        })
7892                    },
7893                    write: |settings_content, value| {
7894                        language_settings_field_mut(settings_content, value, |language, value| {
7895                            language.show_completion_documentation = value;
7896                        })
7897                    },
7898                }),
7899                metadata: None,
7900                files: USER | PROJECT,
7901            }),
7902            SettingsPageItem::SettingItem(SettingItem {
7903                title: "Words",
7904                description: "Controls how words are completed.",
7905                field: Box::new(SettingField {
7906                    json_path: Some("languages.$(language).completions.words"),
7907                    pick: |settings_content| {
7908                        language_settings_field(settings_content, |language| {
7909                            language.completions.as_ref()?.words.as_ref()
7910                        })
7911                    },
7912                    write: |settings_content, value| {
7913                        language_settings_field_mut(settings_content, value, |language, value| {
7914                            language.completions.get_or_insert_default().words = value;
7915                        })
7916                    },
7917                }),
7918                metadata: None,
7919                files: USER | PROJECT,
7920            }),
7921            SettingsPageItem::SettingItem(SettingItem {
7922                title: "Words Min Length",
7923                description: "How many characters has to be in the completions query to automatically show the words-based completions.",
7924                field: Box::new(SettingField {
7925                    json_path: Some("languages.$(language).completions.words_min_length"),
7926                    pick: |settings_content| {
7927                        language_settings_field(settings_content, |language| {
7928                            language.completions.as_ref()?.words_min_length.as_ref()
7929                        })
7930                    },
7931                    write: |settings_content, value| {
7932                        language_settings_field_mut(settings_content, value, |language, value| {
7933                            language
7934                                .completions
7935                                .get_or_insert_default()
7936                                .words_min_length = value;
7937                        })
7938                    },
7939                }),
7940                metadata: None,
7941                files: USER | PROJECT,
7942            }),
7943            SettingsPageItem::SettingItem(SettingItem {
7944                title: "Completion Menu Scrollbar",
7945                description: "When to show the scrollbar in the completion menu.",
7946                field: Box::new(SettingField {
7947                    json_path: Some("editor.completion_menu_scrollbar"),
7948                    pick: |settings_content| {
7949                        settings_content.editor.completion_menu_scrollbar.as_ref()
7950                    },
7951                    write: |settings_content, value| {
7952                        settings_content.editor.completion_menu_scrollbar = value;
7953                    },
7954                }),
7955                metadata: None,
7956                files: USER,
7957            }),
7958            SettingsPageItem::SettingItem(SettingItem {
7959                title: "Completion Detail Alignment",
7960                description: "Whether to align detail text in code completions context menus left or right.",
7961                field: Box::new(SettingField {
7962                    json_path: Some("editor.completion_detail_alignment"),
7963                    pick: |settings_content| {
7964                        settings_content.editor.completion_detail_alignment.as_ref()
7965                    },
7966                    write: |settings_content, value| {
7967                        settings_content.editor.completion_detail_alignment = value;
7968                    },
7969                }),
7970                metadata: None,
7971                files: USER,
7972            }),
7973        ]
7974    }
7975
7976    fn inlay_hints_section() -> [SettingsPageItem; 10] {
7977        [
7978            SettingsPageItem::SectionHeader("Inlay Hints"),
7979            SettingsPageItem::SettingItem(SettingItem {
7980                title: "Enabled",
7981                description: "Global switch to toggle hints on and off.",
7982                field: Box::new(SettingField {
7983                    json_path: Some("languages.$(language).inlay_hints.enabled"),
7984                    pick: |settings_content| {
7985                        language_settings_field(settings_content, |language| {
7986                            language.inlay_hints.as_ref()?.enabled.as_ref()
7987                        })
7988                    },
7989                    write: |settings_content, value| {
7990                        language_settings_field_mut(settings_content, value, |language, value| {
7991                            language.inlay_hints.get_or_insert_default().enabled = value;
7992                        })
7993                    },
7994                }),
7995                metadata: None,
7996                files: USER | PROJECT,
7997            }),
7998            SettingsPageItem::SettingItem(SettingItem {
7999                title: "Show Value Hints",
8000                description: "Global switch to toggle inline values on and off when debugging.",
8001                field: Box::new(SettingField {
8002                    json_path: Some("languages.$(language).inlay_hints.show_value_hints"),
8003                    pick: |settings_content| {
8004                        language_settings_field(settings_content, |language| {
8005                            language.inlay_hints.as_ref()?.show_value_hints.as_ref()
8006                        })
8007                    },
8008                    write: |settings_content, value| {
8009                        language_settings_field_mut(settings_content, value, |language, value| {
8010                            language
8011                                .inlay_hints
8012                                .get_or_insert_default()
8013                                .show_value_hints = value;
8014                        })
8015                    },
8016                }),
8017                metadata: None,
8018                files: USER | PROJECT,
8019            }),
8020            SettingsPageItem::SettingItem(SettingItem {
8021                title: "Show Type Hints",
8022                description: "Whether type hints should be shown.",
8023                field: Box::new(SettingField {
8024                    json_path: Some("languages.$(language).inlay_hints.show_type_hints"),
8025                    pick: |settings_content| {
8026                        language_settings_field(settings_content, |language| {
8027                            language.inlay_hints.as_ref()?.show_type_hints.as_ref()
8028                        })
8029                    },
8030                    write: |settings_content, value| {
8031                        language_settings_field_mut(settings_content, value, |language, value| {
8032                            language.inlay_hints.get_or_insert_default().show_type_hints = value;
8033                        })
8034                    },
8035                }),
8036                metadata: None,
8037                files: USER | PROJECT,
8038            }),
8039            SettingsPageItem::SettingItem(SettingItem {
8040                title: "Show Parameter Hints",
8041                description: "Whether parameter hints should be shown.",
8042                field: Box::new(SettingField {
8043                    json_path: Some("languages.$(language).inlay_hints.show_parameter_hints"),
8044                    pick: |settings_content| {
8045                        language_settings_field(settings_content, |language| {
8046                            language.inlay_hints.as_ref()?.show_parameter_hints.as_ref()
8047                        })
8048                    },
8049                    write: |settings_content, value| {
8050                        language_settings_field_mut(settings_content, value, |language, value| {
8051                            language
8052                                .inlay_hints
8053                                .get_or_insert_default()
8054                                .show_parameter_hints = value;
8055                        })
8056                    },
8057                }),
8058                metadata: None,
8059                files: USER | PROJECT,
8060            }),
8061            SettingsPageItem::SettingItem(SettingItem {
8062                title: "Show Other Hints",
8063                description: "Whether other hints should be shown.",
8064                field: Box::new(SettingField {
8065                    json_path: Some("languages.$(language).inlay_hints.show_other_hints"),
8066                    pick: |settings_content| {
8067                        language_settings_field(settings_content, |language| {
8068                            language.inlay_hints.as_ref()?.show_other_hints.as_ref()
8069                        })
8070                    },
8071                    write: |settings_content, value| {
8072                        language_settings_field_mut(settings_content, value, |language, value| {
8073                            language
8074                                .inlay_hints
8075                                .get_or_insert_default()
8076                                .show_other_hints = value;
8077                        })
8078                    },
8079                }),
8080                metadata: None,
8081                files: USER | PROJECT,
8082            }),
8083            SettingsPageItem::SettingItem(SettingItem {
8084                title: "Show Background",
8085                description: "Show a background for inlay hints.",
8086                field: Box::new(SettingField {
8087                    json_path: Some("languages.$(language).inlay_hints.show_background"),
8088                    pick: |settings_content| {
8089                        language_settings_field(settings_content, |language| {
8090                            language.inlay_hints.as_ref()?.show_background.as_ref()
8091                        })
8092                    },
8093                    write: |settings_content, value| {
8094                        language_settings_field_mut(settings_content, value, |language, value| {
8095                            language.inlay_hints.get_or_insert_default().show_background = value;
8096                        })
8097                    },
8098                }),
8099                metadata: None,
8100                files: USER | PROJECT,
8101            }),
8102            SettingsPageItem::SettingItem(SettingItem {
8103                title: "Edit Debounce Ms",
8104                description: "Whether or not to debounce inlay hints updates after buffer edits (set to 0 to disable debouncing).",
8105                field: Box::new(SettingField {
8106                    json_path: Some("languages.$(language).inlay_hints.edit_debounce_ms"),
8107                    pick: |settings_content| {
8108                        language_settings_field(settings_content, |language| {
8109                            language.inlay_hints.as_ref()?.edit_debounce_ms.as_ref()
8110                        })
8111                    },
8112                    write: |settings_content, value| {
8113                        language_settings_field_mut(settings_content, value, |language, value| {
8114                            language
8115                                .inlay_hints
8116                                .get_or_insert_default()
8117                                .edit_debounce_ms = value;
8118                        })
8119                    },
8120                }),
8121                metadata: None,
8122                files: USER | PROJECT,
8123            }),
8124            SettingsPageItem::SettingItem(SettingItem {
8125                title: "Scroll Debounce Ms",
8126                description: "Whether or not to debounce inlay hints updates after buffer scrolls (set to 0 to disable debouncing).",
8127                field: Box::new(SettingField {
8128                    json_path: Some("languages.$(language).inlay_hints.scroll_debounce_ms"),
8129                    pick: |settings_content| {
8130                        language_settings_field(settings_content, |language| {
8131                            language.inlay_hints.as_ref()?.scroll_debounce_ms.as_ref()
8132                        })
8133                    },
8134                    write: |settings_content, value| {
8135                        language_settings_field_mut(settings_content, value, |language, value| {
8136                            language
8137                                .inlay_hints
8138                                .get_or_insert_default()
8139                                .scroll_debounce_ms = value;
8140                        })
8141                    },
8142                }),
8143                metadata: None,
8144                files: USER | PROJECT,
8145            }),
8146            SettingsPageItem::SettingItem(SettingItem {
8147                title: "Toggle On Modifiers Press",
8148                description: "Toggles inlay hints (hides or shows) when the user presses the modifiers specified.",
8149                field: Box::new(
8150                    SettingField {
8151                        json_path: Some(
8152                            "languages.$(language).inlay_hints.toggle_on_modifiers_press",
8153                        ),
8154                        pick: |settings_content| {
8155                            language_settings_field(settings_content, |language| {
8156                                language
8157                                    .inlay_hints
8158                                    .as_ref()?
8159                                    .toggle_on_modifiers_press
8160                                    .as_ref()
8161                            })
8162                        },
8163                        write: |settings_content, value| {
8164                            language_settings_field_mut(
8165                                settings_content,
8166                                value,
8167                                |language, value| {
8168                                    language
8169                                        .inlay_hints
8170                                        .get_or_insert_default()
8171                                        .toggle_on_modifiers_press = value;
8172                                },
8173                            )
8174                        },
8175                    }
8176                    .unimplemented(),
8177                ),
8178                metadata: None,
8179                files: USER | PROJECT,
8180            }),
8181        ]
8182    }
8183
8184    fn tasks_section() -> [SettingsPageItem; 4] {
8185        [
8186            SettingsPageItem::SectionHeader("Tasks"),
8187            SettingsPageItem::SettingItem(SettingItem {
8188                title: "Enabled",
8189                description: "Whether tasks are enabled for this language.",
8190                field: Box::new(SettingField {
8191                    json_path: Some("languages.$(language).tasks.enabled"),
8192                    pick: |settings_content| {
8193                        language_settings_field(settings_content, |language| {
8194                            language.tasks.as_ref()?.enabled.as_ref()
8195                        })
8196                    },
8197                    write: |settings_content, value| {
8198                        language_settings_field_mut(settings_content, value, |language, value| {
8199                            language.tasks.get_or_insert_default().enabled = value;
8200                        })
8201                    },
8202                }),
8203                metadata: None,
8204                files: USER | PROJECT,
8205            }),
8206            SettingsPageItem::SettingItem(SettingItem {
8207                title: "Variables",
8208                description: "Extra task variables to set for a particular language.",
8209                field: Box::new(
8210                    SettingField {
8211                        json_path: Some("languages.$(language).tasks.variables"),
8212                        pick: |settings_content| {
8213                            language_settings_field(settings_content, |language| {
8214                                language.tasks.as_ref()?.variables.as_ref()
8215                            })
8216                        },
8217                        write: |settings_content, value| {
8218                            language_settings_field_mut(
8219                                settings_content,
8220                                value,
8221                                |language, value| {
8222                                    language.tasks.get_or_insert_default().variables = value;
8223                                },
8224                            )
8225                        },
8226                    }
8227                    .unimplemented(),
8228                ),
8229                metadata: None,
8230                files: USER | PROJECT,
8231            }),
8232            SettingsPageItem::SettingItem(SettingItem {
8233                title: "Prefer LSP",
8234                description: "Use LSP tasks over Zed language extension tasks.",
8235                field: Box::new(SettingField {
8236                    json_path: Some("languages.$(language).tasks.prefer_lsp"),
8237                    pick: |settings_content| {
8238                        language_settings_field(settings_content, |language| {
8239                            language.tasks.as_ref()?.prefer_lsp.as_ref()
8240                        })
8241                    },
8242                    write: |settings_content, value| {
8243                        language_settings_field_mut(settings_content, value, |language, value| {
8244                            language.tasks.get_or_insert_default().prefer_lsp = value;
8245                        })
8246                    },
8247                }),
8248                metadata: None,
8249                files: USER | PROJECT,
8250            }),
8251        ]
8252    }
8253
8254    fn miscellaneous_section() -> [SettingsPageItem; 6] {
8255        [
8256            SettingsPageItem::SectionHeader("Miscellaneous"),
8257            SettingsPageItem::SettingItem(SettingItem {
8258                title: "Word Diff Enabled",
8259                description: "Whether to enable word diff highlighting in the editor. When enabled, changed words within modified lines are highlighted to show exactly what changed.",
8260                field: Box::new(SettingField {
8261                    json_path: Some("languages.$(language).word_diff_enabled"),
8262                    pick: |settings_content| {
8263                        language_settings_field(settings_content, |language| {
8264                            language.word_diff_enabled.as_ref()
8265                        })
8266                    },
8267                    write: |settings_content, value| {
8268                        language_settings_field_mut(settings_content, value, |language, value| {
8269                            language.word_diff_enabled = value;
8270                        })
8271                    },
8272                }),
8273                metadata: None,
8274                files: USER | PROJECT,
8275            }),
8276            SettingsPageItem::SettingItem(SettingItem {
8277                title: "Debuggers",
8278                description: "Preferred debuggers for this language.",
8279                field: Box::new(
8280                    SettingField {
8281                        json_path: Some("languages.$(language).debuggers"),
8282                        pick: |settings_content| {
8283                            language_settings_field(settings_content, |language| {
8284                                language.debuggers.as_ref()
8285                            })
8286                        },
8287                        write: |settings_content, value| {
8288                            language_settings_field_mut(
8289                                settings_content,
8290                                value,
8291                                |language, value| {
8292                                    language.debuggers = value;
8293                                },
8294                            )
8295                        },
8296                    }
8297                    .unimplemented(),
8298                ),
8299                metadata: None,
8300                files: USER | PROJECT,
8301            }),
8302            SettingsPageItem::SettingItem(SettingItem {
8303                title: "Middle Click Paste",
8304                description: "Enable middle-click paste on Linux.",
8305                field: Box::new(SettingField {
8306                    json_path: Some("languages.$(language).editor.middle_click_paste"),
8307                    pick: |settings_content| settings_content.editor.middle_click_paste.as_ref(),
8308                    write: |settings_content, value| {
8309                        settings_content.editor.middle_click_paste = value;
8310                    },
8311                }),
8312                metadata: None,
8313                files: USER,
8314            }),
8315            SettingsPageItem::SettingItem(SettingItem {
8316                title: "Extend Comment On Newline",
8317                description: "Whether to start a new line with a comment when a previous line is a comment as well.",
8318                field: Box::new(SettingField {
8319                    json_path: Some("languages.$(language).extend_comment_on_newline"),
8320                    pick: |settings_content| {
8321                        language_settings_field(settings_content, |language| {
8322                            language.extend_comment_on_newline.as_ref()
8323                        })
8324                    },
8325                    write: |settings_content, value| {
8326                        language_settings_field_mut(settings_content, value, |language, value| {
8327                            language.extend_comment_on_newline = value;
8328                        })
8329                    },
8330                }),
8331                metadata: None,
8332                files: USER | PROJECT,
8333            }),
8334            SettingsPageItem::SettingItem(SettingItem {
8335                title: "Colorize Brackets",
8336                description: "Whether to colorize brackets in the editor.",
8337                field: Box::new(SettingField {
8338                    json_path: Some("languages.$(language).colorize_brackets"),
8339                    pick: |settings_content| {
8340                        language_settings_field(settings_content, |language| {
8341                            language.colorize_brackets.as_ref()
8342                        })
8343                    },
8344                    write: |settings_content, value| {
8345                        language_settings_field_mut(settings_content, value, |language, value| {
8346                            language.colorize_brackets = value;
8347                        })
8348                    },
8349                }),
8350                metadata: None,
8351                files: USER | PROJECT,
8352            }),
8353        ]
8354    }
8355
8356    fn global_only_miscellaneous_sub_section() -> [SettingsPageItem; 3] {
8357        [
8358            SettingsPageItem::SettingItem(SettingItem {
8359                title: "Image Viewer",
8360                description: "The unit for image file sizes.",
8361                field: Box::new(SettingField {
8362                    json_path: Some("image_viewer.unit"),
8363                    pick: |settings_content| {
8364                        settings_content
8365                            .image_viewer
8366                            .as_ref()
8367                            .and_then(|image_viewer| image_viewer.unit.as_ref())
8368                    },
8369                    write: |settings_content, value| {
8370                        settings_content.image_viewer.get_or_insert_default().unit = value;
8371                    },
8372                }),
8373                metadata: None,
8374                files: USER,
8375            }),
8376            SettingsPageItem::SettingItem(SettingItem {
8377                title: "Auto Replace Emoji Shortcode",
8378                description: "Whether to automatically replace emoji shortcodes with emoji characters.",
8379                field: Box::new(SettingField {
8380                    json_path: Some("message_editor.auto_replace_emoji_shortcode"),
8381                    pick: |settings_content| {
8382                        settings_content
8383                            .message_editor
8384                            .as_ref()
8385                            .and_then(|message_editor| {
8386                                message_editor.auto_replace_emoji_shortcode.as_ref()
8387                            })
8388                    },
8389                    write: |settings_content, value| {
8390                        settings_content
8391                            .message_editor
8392                            .get_or_insert_default()
8393                            .auto_replace_emoji_shortcode = value;
8394                    },
8395                }),
8396                metadata: None,
8397                files: USER,
8398            }),
8399            SettingsPageItem::SettingItem(SettingItem {
8400                title: "Drop Size Target",
8401                description: "Relative size of the drop target in the editor that will open dropped file as a split pane.",
8402                field: Box::new(SettingField {
8403                    json_path: Some("drop_target_size"),
8404                    pick: |settings_content| settings_content.workspace.drop_target_size.as_ref(),
8405                    write: |settings_content, value| {
8406                        settings_content.workspace.drop_target_size = value;
8407                    },
8408                }),
8409                metadata: None,
8410                files: USER,
8411            }),
8412        ]
8413    }
8414
8415    let is_global = active_language().is_none();
8416
8417    let lsp_document_colors_item = [SettingsPageItem::SettingItem(SettingItem {
8418        title: "LSP Document Colors",
8419        description: "How to render LSP color previews in the editor.",
8420        field: Box::new(SettingField {
8421            json_path: Some("lsp_document_colors"),
8422            pick: |settings_content| settings_content.editor.lsp_document_colors.as_ref(),
8423            write: |settings_content, value| {
8424                settings_content.editor.lsp_document_colors = value;
8425            },
8426        }),
8427        metadata: None,
8428        files: USER,
8429    })];
8430
8431    if is_global {
8432        concat_sections!(
8433            indentation_section(),
8434            wrapping_section(),
8435            indent_guides_section(),
8436            formatting_section(),
8437            autoclose_section(),
8438            whitespace_section(),
8439            completions_section(),
8440            inlay_hints_section(),
8441            lsp_document_colors_item,
8442            tasks_section(),
8443            miscellaneous_section(),
8444            global_only_miscellaneous_sub_section(),
8445        )
8446    } else {
8447        concat_sections!(
8448            indentation_section(),
8449            wrapping_section(),
8450            indent_guides_section(),
8451            formatting_section(),
8452            autoclose_section(),
8453            whitespace_section(),
8454            completions_section(),
8455            inlay_hints_section(),
8456            tasks_section(),
8457            miscellaneous_section(),
8458        )
8459    }
8460}
8461
8462/// LanguageSettings items that should be included in the "Languages & Tools" page
8463/// not the "Editor" page
8464fn non_editor_language_settings_data() -> Box<[SettingsPageItem]> {
8465    fn lsp_section() -> [SettingsPageItem; 5] {
8466        [
8467            SettingsPageItem::SectionHeader("LSP"),
8468            SettingsPageItem::SettingItem(SettingItem {
8469                title: "Enable Language Server",
8470                description: "Whether to use language servers to provide code intelligence.",
8471                field: Box::new(SettingField {
8472                    json_path: Some("languages.$(language).enable_language_server"),
8473                    pick: |settings_content| {
8474                        language_settings_field(settings_content, |language| {
8475                            language.enable_language_server.as_ref()
8476                        })
8477                    },
8478                    write: |settings_content, value| {
8479                        language_settings_field_mut(settings_content, value, |language, value| {
8480                            language.enable_language_server = value;
8481                        })
8482                    },
8483                }),
8484                metadata: None,
8485                files: USER | PROJECT,
8486            }),
8487            SettingsPageItem::SettingItem(SettingItem {
8488                title: "Language Servers",
8489                description: "The list of language servers to use (or disable) for this language.",
8490                field: Box::new(
8491                    SettingField {
8492                        json_path: Some("languages.$(language).language_servers"),
8493                        pick: |settings_content| {
8494                            language_settings_field(settings_content, |language| {
8495                                language.language_servers.as_ref()
8496                            })
8497                        },
8498                        write: |settings_content, value| {
8499                            language_settings_field_mut(
8500                                settings_content,
8501                                value,
8502                                |language, value| {
8503                                    language.language_servers = value;
8504                                },
8505                            )
8506                        },
8507                    }
8508                    .unimplemented(),
8509                ),
8510                metadata: None,
8511                files: USER | PROJECT,
8512            }),
8513            SettingsPageItem::SettingItem(SettingItem {
8514                title: "Linked Edits",
8515                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.",
8516                field: Box::new(SettingField {
8517                    json_path: Some("languages.$(language).linked_edits"),
8518                    pick: |settings_content| {
8519                        language_settings_field(settings_content, |language| {
8520                            language.linked_edits.as_ref()
8521                        })
8522                    },
8523                    write: |settings_content, value| {
8524                        language_settings_field_mut(settings_content, value, |language, value| {
8525                            language.linked_edits = value;
8526                        })
8527                    },
8528                }),
8529                metadata: None,
8530                files: USER | PROJECT,
8531            }),
8532            SettingsPageItem::SettingItem(SettingItem {
8533                title: "Go To Definition Fallback",
8534                description: "Whether to follow-up empty Go to definition responses from the language server.",
8535                field: Box::new(SettingField {
8536                    json_path: Some("go_to_definition_fallback"),
8537                    pick: |settings_content| {
8538                        settings_content.editor.go_to_definition_fallback.as_ref()
8539                    },
8540                    write: |settings_content, value| {
8541                        settings_content.editor.go_to_definition_fallback = value;
8542                    },
8543                }),
8544                metadata: None,
8545                files: USER,
8546            }),
8547        ]
8548    }
8549
8550    fn lsp_completions_section() -> [SettingsPageItem; 4] {
8551        [
8552            SettingsPageItem::SectionHeader("LSP Completions"),
8553            SettingsPageItem::SettingItem(SettingItem {
8554                title: "Enabled",
8555                description: "Whether to fetch LSP completions or not.",
8556                field: Box::new(SettingField {
8557                    json_path: Some("languages.$(language).completions.lsp"),
8558                    pick: |settings_content| {
8559                        language_settings_field(settings_content, |language| {
8560                            language.completions.as_ref()?.lsp.as_ref()
8561                        })
8562                    },
8563                    write: |settings_content, value| {
8564                        language_settings_field_mut(settings_content, value, |language, value| {
8565                            language.completions.get_or_insert_default().lsp = value;
8566                        })
8567                    },
8568                }),
8569                metadata: None,
8570                files: USER | PROJECT,
8571            }),
8572            SettingsPageItem::SettingItem(SettingItem {
8573                title: "Fetch Timeout (milliseconds)",
8574                description: "When fetching LSP completions, determines how long to wait for a response of a particular server (set to 0 to wait indefinitely).",
8575                field: Box::new(SettingField {
8576                    json_path: Some("languages.$(language).completions.lsp_fetch_timeout_ms"),
8577                    pick: |settings_content| {
8578                        language_settings_field(settings_content, |language| {
8579                            language.completions.as_ref()?.lsp_fetch_timeout_ms.as_ref()
8580                        })
8581                    },
8582                    write: |settings_content, value| {
8583                        language_settings_field_mut(settings_content, value, |language, value| {
8584                            language
8585                                .completions
8586                                .get_or_insert_default()
8587                                .lsp_fetch_timeout_ms = value;
8588                        })
8589                    },
8590                }),
8591                metadata: None,
8592                files: USER | PROJECT,
8593            }),
8594            SettingsPageItem::SettingItem(SettingItem {
8595                title: "Insert Mode",
8596                description: "Controls how LSP completions are inserted.",
8597                field: Box::new(SettingField {
8598                    json_path: Some("languages.$(language).completions.lsp_insert_mode"),
8599                    pick: |settings_content| {
8600                        language_settings_field(settings_content, |language| {
8601                            language.completions.as_ref()?.lsp_insert_mode.as_ref()
8602                        })
8603                    },
8604                    write: |settings_content, value| {
8605                        language_settings_field_mut(settings_content, value, |language, value| {
8606                            language.completions.get_or_insert_default().lsp_insert_mode = value;
8607                        })
8608                    },
8609                }),
8610                metadata: None,
8611                files: USER | PROJECT,
8612            }),
8613        ]
8614    }
8615
8616    fn debugger_section() -> [SettingsPageItem; 2] {
8617        [
8618            SettingsPageItem::SectionHeader("Debuggers"),
8619            SettingsPageItem::SettingItem(SettingItem {
8620                title: "Debuggers",
8621                description: "Preferred debuggers for this language.",
8622                field: Box::new(
8623                    SettingField {
8624                        json_path: Some("languages.$(language).debuggers"),
8625                        pick: |settings_content| {
8626                            language_settings_field(settings_content, |language| {
8627                                language.debuggers.as_ref()
8628                            })
8629                        },
8630                        write: |settings_content, value| {
8631                            language_settings_field_mut(
8632                                settings_content,
8633                                value,
8634                                |language, value| {
8635                                    language.debuggers = value;
8636                                },
8637                            )
8638                        },
8639                    }
8640                    .unimplemented(),
8641                ),
8642                metadata: None,
8643                files: USER | PROJECT,
8644            }),
8645        ]
8646    }
8647
8648    fn prettier_section() -> [SettingsPageItem; 5] {
8649        [
8650            SettingsPageItem::SectionHeader("Prettier"),
8651            SettingsPageItem::SettingItem(SettingItem {
8652                title: "Allowed",
8653                description: "Enables or disables formatting with Prettier for a given language.",
8654                field: Box::new(SettingField {
8655                    json_path: Some("languages.$(language).prettier.allowed"),
8656                    pick: |settings_content| {
8657                        language_settings_field(settings_content, |language| {
8658                            language.prettier.as_ref()?.allowed.as_ref()
8659                        })
8660                    },
8661                    write: |settings_content, value| {
8662                        language_settings_field_mut(settings_content, value, |language, value| {
8663                            language.prettier.get_or_insert_default().allowed = value;
8664                        })
8665                    },
8666                }),
8667                metadata: None,
8668                files: USER | PROJECT,
8669            }),
8670            SettingsPageItem::SettingItem(SettingItem {
8671                title: "Parser",
8672                description: "Forces Prettier integration to use a specific parser name when formatting files with the language.",
8673                field: Box::new(SettingField {
8674                    json_path: Some("languages.$(language).prettier.parser"),
8675                    pick: |settings_content| {
8676                        language_settings_field(settings_content, |language| {
8677                            language.prettier.as_ref()?.parser.as_ref()
8678                        })
8679                    },
8680                    write: |settings_content, value| {
8681                        language_settings_field_mut(settings_content, value, |language, value| {
8682                            language.prettier.get_or_insert_default().parser = value;
8683                        })
8684                    },
8685                }),
8686                metadata: None,
8687                files: USER | PROJECT,
8688            }),
8689            SettingsPageItem::SettingItem(SettingItem {
8690                title: "Plugins",
8691                description: "Forces Prettier integration to use specific plugins when formatting files with the language.",
8692                field: Box::new(
8693                    SettingField {
8694                        json_path: Some("languages.$(language).prettier.plugins"),
8695                        pick: |settings_content| {
8696                            language_settings_field(settings_content, |language| {
8697                                language.prettier.as_ref()?.plugins.as_ref()
8698                            })
8699                        },
8700                        write: |settings_content, value| {
8701                            language_settings_field_mut(
8702                                settings_content,
8703                                value,
8704                                |language, value| {
8705                                    language.prettier.get_or_insert_default().plugins = value;
8706                                },
8707                            )
8708                        },
8709                    }
8710                    .unimplemented(),
8711                ),
8712                metadata: None,
8713                files: USER | PROJECT,
8714            }),
8715            SettingsPageItem::SettingItem(SettingItem {
8716                title: "Options",
8717                description: "Default Prettier options, in the format as in package.json section for Prettier.",
8718                field: Box::new(
8719                    SettingField {
8720                        json_path: Some("languages.$(language).prettier.options"),
8721                        pick: |settings_content| {
8722                            language_settings_field(settings_content, |language| {
8723                                language.prettier.as_ref()?.options.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().options = value;
8732                                },
8733                            )
8734                        },
8735                    }
8736                    .unimplemented(),
8737                ),
8738                metadata: None,
8739                files: USER | PROJECT,
8740            }),
8741        ]
8742    }
8743
8744    concat_sections!(
8745        lsp_section(),
8746        lsp_completions_section(),
8747        debugger_section(),
8748        prettier_section(),
8749    )
8750}
8751
8752fn edit_prediction_language_settings_section() -> [SettingsPageItem; 4] {
8753    [
8754        SettingsPageItem::SectionHeader("Edit Predictions"),
8755        SettingsPageItem::SubPageLink(SubPageLink {
8756            title: "Configure Providers".into(),
8757            r#type: Default::default(),
8758            json_path: Some("edit_predictions.providers"),
8759            description: Some("Set up different edit prediction providers in complement to Zed's built-in Zeta model.".into()),
8760            in_json: false,
8761            files: USER,
8762            render: render_edit_prediction_setup_page
8763        }),
8764        SettingsPageItem::SettingItem(SettingItem {
8765            title: "Show Edit Predictions",
8766            description: "Controls whether edit predictions are shown immediately or manually.",
8767            field: Box::new(SettingField {
8768                json_path: Some("languages.$(language).show_edit_predictions"),
8769                pick: |settings_content| {
8770                    language_settings_field(settings_content, |language| {
8771                        language.show_edit_predictions.as_ref()
8772                    })
8773                },
8774                write: |settings_content, value| {
8775                    language_settings_field_mut(settings_content, value, |language, value| {
8776                        language.show_edit_predictions = value;
8777                    })
8778                },
8779            }),
8780            metadata: None,
8781            files: USER | PROJECT,
8782        }),
8783        SettingsPageItem::SettingItem(SettingItem {
8784            title: "Disable in Language Scopes",
8785            description: "Controls whether edit predictions are shown in the given language scopes.",
8786            field: Box::new(
8787                SettingField {
8788                    json_path: Some("languages.$(language).edit_predictions_disabled_in"),
8789                    pick: |settings_content| {
8790                        language_settings_field(settings_content, |language| {
8791                            language.edit_predictions_disabled_in.as_ref()
8792                        })
8793                    },
8794                    write: |settings_content, value| {
8795                        language_settings_field_mut(settings_content, value, |language, value| {
8796                            language.edit_predictions_disabled_in = value;
8797                        })
8798                    },
8799                }
8800                .unimplemented(),
8801            ),
8802            metadata: None,
8803            files: USER | PROJECT,
8804        }),
8805    ]
8806}
8807
8808fn show_scrollbar_or_editor(
8809    settings_content: &SettingsContent,
8810    show: fn(&SettingsContent) -> Option<&settings::ShowScrollbar>,
8811) -> Option<&settings::ShowScrollbar> {
8812    show(settings_content).or(settings_content
8813        .editor
8814        .scrollbar
8815        .as_ref()
8816        .and_then(|scrollbar| scrollbar.show.as_ref()))
8817}
8818
8819fn dynamic_variants<T>() -> &'static [T::Discriminant]
8820where
8821    T: strum::IntoDiscriminant,
8822    T::Discriminant: strum::VariantArray,
8823{
8824    <<T as strum::IntoDiscriminant>::Discriminant as strum::VariantArray>::VARIANTS
8825}