page_data.rs

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