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