page_data.rs

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